#include "StdAfx.h"
#include "CryGameSpyLobby.h"
#include "CryGameSpyStats.h"
#include "../CrySharedLobbyPacket.h"

#if USE_CRYLOBBY_GAMESPY

#define STATS_PARAM_READ_LEADERBOARD_ENTRY				0	
#define STATS_PARAM_OUT_LEADERBOARD_RESULT				1
#define STATS_PARAM_OUT_LEADERBOARD_ROWS					2
#define STATS_PARAM_OUT_LEADERBOARD_COLUMNS				3
#define STATS_PARAM_IN_PROFILE_FILTER							4

#define STATS_PARAM_WRITE_LEADERBOARD_USERS				0
#define STATS_PARAM_WRITE_LEADERBOARD_INFO				1
#define STATS_PARAM_WRITE_LEADERBOARD_BOARDS			2

#define STATS_PARAM_READ_USERDATA_ENTRY						0
#define STATS_PARAM_READ_USERDATA_RESULTS					1
#define STATS_PARAM_READ_USERDATA_DECRYPT_BUFFER	2
#define STATS_PARAM_READ_USERDATA_USER						3

#define STATS_PARAM_WRITE_USERDATA_USERS					0
#define STATS_PARAM_WRITE_USERDATA_INFO						1
#define STATS_PARAM_WRITE_USERDATA_WRITES					2
#define STATS_PARAM_WRITE_USERDATA_DATA						3
#define STATS_PARAM_WRITE_USERDATA_ENCRYPT_BUFFER	4

#define GAMESPY_RESERVED_FIELDS				5
#define GAMESPY_LEADERBOARD_TOP_CACHE	50

#define LENGTH_OF_PROFILE_AS_STRING 13

// Following strings are values used to communicate with gamespy backend (they are not to be translated!)

const char* pGameSpyFieldRowName="row";
const char* pGameSpyFieldP2POwnerIDName="ownerid";
const char* pGameSpyFieldDedicatedOwnerIDName="PROFILE";
const char* pGameSpyFieldOwnerNameName="NAME";
const char* pGameSpyFieldRecordName="recordid";
const char* pGameSpyFieldScoreName="SCORE";
const char* pGameSpyStatsFieldName="DATA";
const char* pGameSpyStatsOrder="recordid";
const char* pGameSpyFieldNameFormat="FIELD%d";
const char* pGameSpyTableOrder="SCORE desc";
const char* pGameSpySearchP2PFormat="ownerid = %d";
const char* pGameSpySearchDedicatedFormat="PROFILE = %d";
const char* pGameSpyRecordFilter="";

CCryGameSpyStats::CCryGameSpyStats(CCryLobby* pLobby, CCryLobbyService* pService) : CCryStats(pLobby, pService)
{
	// Make the CCryStats base pointers point to our data so we can use the common code in CCryStats
	for (uint32 i = 0; i < MAX_STATS_TASKS; i++)
	{
		CCryStats::m_pTask[i] = &m_task[i];
	}

	m_numLeaderBoards = 0;
	m_numUserData = 0;
	m_nCumulativeUserDataSize = 0;
	m_pGameSpyTableP2PNameFormat = NULL;
	m_pGameSpyTableDedicatedNameFormat = NULL;
	m_pGameSpyStatsP2PTableName = NULL;
	m_pGameSpyStatsDedicatedTableName = NULL;
	m_sake = NULL;
	m_leaderboardType = eCLLT_P2P;
}

ECryLobbyError CCryGameSpyStats::StartSake()
{
	ECryLobbyError error = eCLE_Success;

	if (m_sake != NULL)
	{
		return eCLE_AlreadyInitialised;
	}

	SAKEStartupResult sakeResult = sakeStartup(&m_sake);
	
	switch (sakeResult)
	{
	default:																// Fall through by design
	case SAKEStartupResult_NOT_AVAILABLE:
		error = eCLE_InternalError;
		break;
	case SAKEStartupResult_SUCCESS:
		error = eCLE_Success;
		break;
	case SAKEStartupResult_CORE_SHUTDOWN:
		error = eCLE_NotInitialised;
		break;
	case SAKEStartupResult_OUT_OF_MEMORY:
		error = eCLE_OutOfMemory;
		break;
	}

	if (error == eCLE_Success)
	{
		SConfigurationParams	neededInfo[ 3 ] = {{CLCC_GAMESPY_TITLE,{NULL}},{CLCC_GAMESPY_GAMEID,{NULL}},{CLCC_GAMESPY_SECRETKEY,{NULL}}};
			
		m_pLobby->GetConfigurationInformation( neededInfo, 3 );

		sakeSetGame(m_sake,(const char *)neededInfo[0].m_pData,neededInfo[1].m_32,(const char *)neededInfo[2].m_pData);
	}

	return error;
}

ECryLobbyError CCryGameSpyStats::SetProfile(int profileId,const char* pLoginTicket)
{
	m_profileId = profileId;

	sakeSetProfile(m_sake,profileId,pLoginTicket);

	ResetRecordData();			// New user signed in, reset the leaderboard record memory for this user

	return eCLE_Success;
}

ECryLobbyError CCryGameSpyStats::Initialise()
{
	SConfigurationParams	neededInfo[ eCLLT_Num ] =
	{
		{ CLCC_GAMESPY_P2PSTATSTABLE,				{ NULL } },
		{ CLCC_GAMESPY_DEDICATEDSTATSTABLE,	{ NULL } }
	};

	m_pLobby->GetConfigurationInformation( neededInfo, 2 );
	m_pGameSpyStatsP2PTableName = static_cast< const char* >( neededInfo[ 0 ].m_pData );
	m_pGameSpyStatsDedicatedTableName = static_cast< const char* >( neededInfo[ 1 ].m_pData );

	return CCryStats::Initialise();
}

ECryLobbyError CCryGameSpyStats::Terminate()
{
	int a;
	int b;
	ECryLobbyError error = CCryStats::Terminate();

	if (error == eCLE_Success)
	{
		if (m_sake != NULL)
		{
			sakeShutdown(m_sake);
		}
	}

	for (a=0;a<m_numLeaderBoards;a++)
	{
		for ( b = 0; b < eCLLT_Num; ++b )
		{
			SAFE_DELETE_ARRAY(m_leaderBoards[a].m_boards[b].m_pColumns);
		}
	}

	return error;
}

void CCryGameSpyStats::Tick(CTimeValue tv)
{
	for ( uint32 i = 0; i < MAX_STATS_TASKS; i++ )
	{
		STask*					pTask = &m_task[ i ];

#if ENABLE_CRYLOBBY_DEBUG_TESTS
		if (pTask->used)
		{
			if (m_pLobby->DebugTickCallStartTaskRunning(pTask->lTaskID))
			{
				StartTaskRunning(i);
				continue;
			}
			if (!m_pLobby->DebugOKToTickTask(pTask->lTaskID, pTask->running))
			{
				continue;
			}
		}
#endif

		if ( pTask->used && pTask->running )
		{
			if ((!pTask->m_taskStarted) && pTask->m_haveGPConnection)
			{
				pTask->m_taskStarted=true;
				switch ( pTask->startedTask )
				{
				case eT_StatsWriteLeaderBoard:
					StartStatsWriteLeaderBoard(i);
					break;

				case eT_StatsReadLeaderBoard:
					StartStatsReadLeaderBoard(i);
					break;

				case eT_StatsReadUserData:
					StartStatsReadUserData(i);
					break;

				case eT_StatsWriteUserData:
					StartStatsWriteUserData(i);
					break;
				}
			}

			if ( pTask->error != eCLE_Success )
			{
				StopTaskRunning( i );
			}
		}
	}
}

ECryLobbyError CCryGameSpyStats::StartTask(ETask etask, bool startRunning, uint32 user, CryStatsTaskID* pSTaskID, CryLobbyTaskID* pLTaskID, CrySessionHandle h, void* pCb, void* pCbArg)
{
	CryStatsTaskID tmpSTaskID;
	CryStatsTaskID* pUseSTaskID = pSTaskID ? pSTaskID : &tmpSTaskID;
	ECryLobbyError error = CCryStats::StartTask(etask, startRunning, pUseSTaskID, pLTaskID, h, pCb, pCbArg);

	if (error == eCLE_Success)
	{
		STask* pTask = &m_task[*pUseSTaskID];

		pTask->m_sakeRequest=NULL;
		pTask->m_haveGPConnection = GetService()->HaveGPConnection();
		pTask->m_taskStarted=false;		// Used to hold the task until the various external gamespy services are ready (for tasks that require this)
	}

	return error;
}

void CCryGameSpyStats::StartTaskRunning(CryStatsTaskID sTaskID)
{
	LOBBY_AUTO_LOCK;

	STask* pTask = &m_task[sTaskID];

	if (pTask->used)
	{
#if ENABLE_CRYLOBBY_DEBUG_TESTS
		ECryLobbyError error;

		if (!m_pLobby->DebugOKToStartTaskRunning(pTask->lTaskID))
		{
			return;
		}

		if (m_pLobby->DebugGenerateError(pTask->lTaskID, error))
		{
			UpdateTaskError(sTaskID, error);
			StopTaskRunning(sTaskID);
			return;
		}
#endif

		pTask->running = true;

		switch (pTask->startedTask)
		{
			case eT_StatsRegisterLeaderBoards:
			case eT_StatsRegisterUserData:
				StopTaskRunning(sTaskID);
			break;

			case eT_StatsWriteLeaderBoard:
				GetService()->SetNeedGPConnection(true);
			break;

			case eT_StatsReadLeaderBoard:
				GetService()->SetNeedGPConnection(true);
			break;

			case eT_StatsReadUserData:
				GetService()->SetNeedGPConnection(true);
			break;

			case eT_StatsWriteUserData:
				GetService()->SetNeedGPConnection(true);
			break;
		}
	}
}

void CCryGameSpyStats::EndTask(CryStatsTaskID sTaskID)
{
	LOBBY_AUTO_LOCK;

	STask* pTask = &m_task[sTaskID];

	if (pTask->used)
	{
		if (pTask->pCb)
		{
			switch (pTask->startedTask)
			{
				case eT_StatsRegisterLeaderBoards:				// Fall through by design
				case eT_StatsWriteLeaderBoard:						// Fall through by design
				case eT_StatsRegisterUserData:						// Fall through by design
				case eT_StatsWriteUserData:								// Fall through by design
					((CryStatsCallback)pTask->pCb)(pTask->lTaskID, pTask->error, pTask->pCbArg);
				break;

				case eT_StatsReadLeaderBoard:				// nothing to do
					EndStatsReadLeaderBoard(sTaskID);
				break;

				case eT_StatsReadUserData:
					EndStatsReadUserData(sTaskID);
				break;
			}
		}

		NetLog("[Lobby] Stats EndTask %d (%d) Result %d", pTask->startedTask, pTask->subTask, pTask->error);

		FreeTask(sTaskID);
	}
}

void CCryGameSpyStats::StopTaskRunning(CryStatsTaskID sTaskID)
{
	STask* pTask = &m_task[sTaskID];

	if (pTask->used)
	{
		pTask->running = false;

		TO_GAME_FROM_LOBBY(&CCryGameSpyStats::EndTask, this, sTaskID);
	}
}


ECryLobbyError CCryGameSpyStats::StatsRegisterLeaderBoards(SCryStatsLeaderBoardWrite* pBoards, uint32 numBoards, CryLobbyTaskID* pTaskID, CryStatsCallback cb, void* pCbArg)
{
	ECryLobbyError error = eCLE_Success;

	LOBBY_AUTO_LOCK;

	SConfigurationParams	neededInfo[ eCLLT_Num ] =
	{
		{ CLCC_GAMESPY_P2PLEADERBOARDFMT,				{ NULL } },
		{ CLCC_GAMESPY_DEDICATEDLEADERBOARDFMT,	{ NULL } }
	};

	CryStatsTaskID sTaskID;

	m_pLobby->GetConfigurationInformation( neededInfo, eCLLT_Num );
	m_pGameSpyTableP2PNameFormat = static_cast< const char* >( neededInfo[ 0 ].m_pData );
	m_pGameSpyTableDedicatedNameFormat = static_cast< const char* >( neededInfo[ 1 ].m_pData );

	error = StartTask(eT_StatsRegisterLeaderBoards, false, 0, &sTaskID, pTaskID, CrySessionInvalidHandle, (void*)cb, pCbArg);

	if (error == eCLE_Success)
	{
		if ((m_numLeaderBoards + numBoards) > m_leaderBoards.size())
		{
			m_leaderBoards.resize(m_leaderBoards.size() + numBoards);
		}

		for (uint32 i = 0; i < numBoards; i++)
		{
			for (uint32 j = 0; j < eCLLT_Num; ++j )
			{
				SRegisterLeaderBoardData* pRegisterData = &m_leaderBoards[i].m_boards[j];
				SCryStatsLeaderBoardWrite* pBoardData = &pBoards[i];

				pRegisterData->m_recordIdCache.clear();
				pRegisterData->m_recordCount = 0;
				pRegisterData->m_boardID = pBoardData->id;
				pRegisterData->m_tableID.Format(static_cast< const char* >( neededInfo[ j ].m_pData ),pBoardData->id + 1);		// Our Indices start at 1
				pRegisterData->m_numColumns = pBoardData->data.numColumns;

				if (pRegisterData->m_numColumns > 0)
				{
					if ( pRegisterData->m_numColumns <= MAX_GAMESPY_LEADERBOARD_FIELDS - GAMESPY_RESERVED_FIELDS )
					{
						pRegisterData->m_pColumns = new SRegisterLeaderBoardData::SColumn[pRegisterData->m_numColumns];

						for (uint32 k = 0; k < pRegisterData->m_numColumns; k++)
						{
							SRegisterLeaderBoardData::SColumn* pRegisterColumn = &pRegisterData->m_pColumns[k];
							SCryStatsLeaderBoardUserColumn* pBoardColumn = &pBoardData->data.pColumns[k];

							pRegisterColumn->m_columnID = pBoardColumn->columnID;
							pRegisterColumn->m_fieldID.Format(pGameSpyFieldNameFormat,pBoardColumn->columnID + 1);	// Our indices start at 1
							pRegisterColumn->m_dataID = pBoardColumn->data.m_id;
							pRegisterColumn->m_dataType = pBoardColumn->data.m_type;

							if (pBoardColumn->data.m_type != eCLUDT_Int64)
							{
								return eCLE_InvalidParam;			// We only support 64 bit field types at present
							}
						}
					}
					else
					{
						return eCLE_InvalidParam;
					}
				}
				else
				{
					pRegisterData->m_pColumns = NULL;
				}
			}

			m_numLeaderBoards++;
		}

		FROM_GAME_TO_LOBBY(&CCryGameSpyStats::StartTaskRunning, this, sTaskID);
	}

	NetLog("[Lobby] Start StatsRegisterLeaderBoards return %d", error);

	return error;
}

ECryLobbyError CCryGameSpyStats::StatsWriteLeaderBoards(CrySessionHandle session, uint32 user, SCryStatsLeaderBoardWrite* pBoards, uint32 numBoards, CryLobbyTaskID* pTaskID, CryStatsCallback cb, void* pCbArg)
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError	error = eCLE_Success;
	CryStatsTaskID	sTaskID;

	error = StartTask( eT_StatsWriteLeaderBoard, false, 0, &sTaskID, pTaskID, CrySessionInvalidHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		error = CreateTaskParamMem( sTaskID, STATS_PARAM_WRITE_LEADERBOARD_USERS, NULL, sizeof ( SUser ) );

		if ( error = eCLE_Success )
		{
			SUser*					pUsers = ( SUser* )m_pLobby->MemGetPtr( m_task[ sTaskID ].paramsMem[ STATS_PARAM_WRITE_LEADERBOARD_USERS ] );

			pUsers->m_info = eUIB_LocalIndex;
			pUsers->m_localIndex = user;
			error = StatsWriteLeaderBoards( sTaskID, &pBoards, &numBoards, 1 );
		}
	}

	if ( error != eCLE_Success )
	{
		FreeTask( sTaskID );
	}

	return error;
}

ECryLobbyError CCryGameSpyStats::StatsWriteLeaderBoards(CrySessionHandle session, CryUserID* pUserIDs, SCryStatsLeaderBoardWrite** ppBoards, uint32* pNumBoards, uint32 numUsers, CryLobbyTaskID* pTaskID, CryStatsCallback cb, void* pCbArg)
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError	error = eCLE_Success;
	CryStatsTaskID	sTaskID;

	error = StartTask( eT_StatsWriteLeaderBoard, false, 0, &sTaskID, pTaskID, CrySessionInvalidHandle, ( void* )cb, pCbArg );

	if ( error == eCLE_Success )
	{
		error = CreateTaskParamMem( sTaskID, STATS_PARAM_WRITE_LEADERBOARD_USERS, NULL, numUsers * sizeof ( SUser ) );

		if ( error == eCLE_Success )
		{
			SUser*					pUsers = ( SUser* )m_pLobby->MemGetPtr( m_task[ sTaskID ].paramsMem[ STATS_PARAM_WRITE_LEADERBOARD_USERS ] );

			for ( uint32 i = 0; i < numUsers; ++i )
			{
				pUsers[ i ].m_info = eUIB_GPProfile;
				pUsers[ i ].m_profileId = static_cast< const SCryGameSpyUserID* >( pUserIDs[ i ].get() )->m_profileID;
			}

			error = StatsWriteLeaderBoards( sTaskID, ppBoards, pNumBoards, numUsers );
		}
	}

	if ( error != eCLE_Success )
	{
		FreeTask( sTaskID );
	}

	return error;
}

ECryLobbyError CCryGameSpyStats::StatsWriteLeaderBoards( CryStatsTaskID	sTaskID, SCryStatsLeaderBoardWrite** ppBoards, uint32* pNumBoards, uint32 numUsers )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError	error = eCLE_Success;

	STask*					pTask = &m_task[ sTaskID ];

	if ( gEnv->IsDedicated() )
	{
		pTask->m_leaderboardType = eCLLT_Dedicated;
	}
	else
	{
		pTask->m_leaderboardType = eCLLT_P2P;
	}

	error = CreateTaskParamMem( sTaskID, STATS_PARAM_WRITE_LEADERBOARD_INFO, NULL, sizeof ( SCryGameSpyStatsBoardWriteInfo ) );

	if ( error == eCLE_Success )
	{
		SCryGameSpyStatsBoardWriteInfo* pWriteInfo = ( SCryGameSpyStatsBoardWriteInfo* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_LEADERBOARD_INFO ] );

		pWriteInfo->m_numBoards = 0;
		pWriteInfo->m_curBoard = 0;
		pWriteInfo->m_pLeaderBoards = NULL;
		error = BuildLeaderBoardWriteArray( sTaskID, numUsers, 0, ppBoards, pNumBoards, 0, &pWriteInfo->m_pLeaderBoards, &pWriteInfo->m_numBoards );

		if ( error == eCLE_Success )
		{
			FROM_GAME_TO_LOBBY( &CCryGameSpyStats::StartTaskRunning, this, sTaskID );
		}
	}

	return error;
}

ECryLobbyError CCryGameSpyStats::BuildLeaderBoardWriteArray( CryStatsTaskID sTaskID, uint32 numUsers, uint32 userIdx, SCryStatsLeaderBoardWrite** ppBoards, uint32* pNumBoards, uint32 boardIdx, SCryGameSpyOneLeaderBoardWrite** ppWrites, uint32* pNumWrites )
{
	ECryLobbyError	error = eCLE_Success;
	STask*					pTask = &m_task[ sTaskID ];

	if ( userIdx == numUsers )
	{
		error = CreateTaskParamMem( sTaskID, STATS_PARAM_WRITE_LEADERBOARD_BOARDS, NULL, *pNumWrites * sizeof ( SCryGameSpyOneLeaderBoardWrite ) );

		if ( error == eCLE_Success )
		{
			*ppWrites = ( SCryGameSpyOneLeaderBoardWrite* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_LEADERBOARD_BOARDS ] ) + *pNumWrites;
		}
	}
	else
	{
		SCryStatsLeaderBoardWrite*	pInBoard = NULL;
		SRegisterLeaderBoardData*		pRegisteredBoard = NULL;

		if ( boardIdx < pNumBoards[ userIdx ] )
		{
			if ( ppBoards[ userIdx ] )
			{
				pInBoard = &ppBoards[ userIdx ][ boardIdx ];
				pRegisteredBoard = FindRegisteredBoardInfo( pInBoard->id, pTask->m_leaderboardType );
				++boardIdx;
			}

			if ( pRegisteredBoard )
			{
				++*pNumWrites;
			}
			else
			{
				error = eCLE_InvalidParam;
			}
		}
		else
		{
			++userIdx;
			boardIdx = 0;
		}

		if ( error == eCLE_Success )
		{
			error = BuildLeaderBoardWriteArray( sTaskID, numUsers, userIdx, ppBoards, pNumBoards, boardIdx, ppWrites, pNumWrites );

			if ( ( error == eCLE_Success ) && pRegisteredBoard )
			{
				uint32											j = 0;

				--*ppWrites;
				( *ppWrites )->m_leaderboardType = pTask->m_leaderboardType;
				( *ppWrites )->m_userIdx = userIdx;
				( *ppWrites )->m_sakeCreateInput.mTableId = const_cast< char* >( pRegisteredBoard->m_tableID.c_str() );
				( *ppWrites )->m_sakeUpdateInput.mTableId = const_cast< char* >( pRegisteredBoard->m_tableID.c_str() );
				( *ppWrites )->m_sakeCreateInput.mNumFields = pInBoard->data.numColumns + 2;	// SCORE and NAME are columns under GameSpy
				( *ppWrites )->m_sakeUpdateInput.mNumFields = pInBoard->data.numColumns + 2;	// SCORE and NAME are columns under GameSpy
				( *ppWrites )->m_sakeCreateInput.mFields = ( *ppWrites )->m_sakeField;
				( *ppWrites )->m_sakeUpdateInput.mFields = ( *ppWrites )->m_sakeField;

				if ( pTask->m_leaderboardType == eCLLT_Dedicated )
				{
					// Profile also needs to go in a field for dedicated server leaderboards.
					( *ppWrites )->m_sakeCreateInput.mNumFields++;
					( *ppWrites )->m_sakeUpdateInput.mNumFields++;
					( *ppWrites )->m_sakeField[ j ].mName = const_cast< char* >( pGameSpyFieldDedicatedOwnerIDName );
					( *ppWrites )->m_sakeField[ j ].mType = SAKEFieldType_INT;
					++j;
				}

				( *ppWrites )->m_sakeField[ j ].mName = const_cast< char* >( pGameSpyFieldOwnerNameName );
				( *ppWrites )->m_sakeField[ j ].mType = SAKEFieldType_ASCII_STRING;
				++j;

				for ( uint32 i = 0; i < pInBoard->data.numColumns; ++i, ++j )
				{
					SRegisterLeaderBoardData::SColumn* pColumn = FindColumnFromID( pRegisteredBoard, pInBoard->data.pColumns[ i ].data.m_id );

					if ( pColumn == NULL )
					{
						error = eCLE_InvalidParam;
						break;
					}
					else
					{
						( *ppWrites )->m_sakeField[ j ].mName = const_cast< char* >( pColumn->m_fieldID.c_str() );
						( *ppWrites )->m_sakeField[ j ].mType = SAKEFieldType_INT64;
						( *ppWrites )->m_sakeField[ j ].mValue.mInt64 = pInBoard->data.pColumns[ i ].data.m_int64;
					}
				}

				// Add score field
				( *ppWrites )->m_sakeField[ j ].mName = const_cast< char* >( pGameSpyFieldScoreName );
				( *ppWrites )->m_sakeField[ j ].mType = SAKEFieldType_INT64;
				( *ppWrites )->m_sakeField[ j ].mValue.mInt64 = pInBoard->data.score.score;
			}
		}
	}

	return error;
}

bool CCryGameSpyStats::QueueNextLeaderBoard(CryStatsTaskID sTaskID, SCryGameSpyStatsBoardWriteInfo* pWriteInfo,SAKERequest* pRequest)
{
	if ( pWriteInfo->m_curBoard < pWriteInfo->m_numBoards )
	{
		STask* pTask = &m_task[ sTaskID ];
		SUser* pUsers = ( SUser* )m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_WRITE_LEADERBOARD_USERS]);
		SCryGameSpyOneLeaderBoardWrite* pCurBoard = &pWriteInfo->m_pLeaderBoards[pWriteInfo->m_curBoard];
		SRegisterLeaderBoardData* pRegisteredBoard = FindRegisteredBoardInfo(pCurBoard->m_sakeCreateInput.mTableId);

		if (pRegisteredBoard == NULL)
		{
			*pRequest = NULL;
			UpdateTaskError( sTaskID, eCLE_InternalError );
		}
		else
		{
			if ( !( pUsers[ pCurBoard->m_userIdx ].m_info & eUIB_GPProfile ) )
			{
				CryUserID	userID = GetService()->GetUserID( pUsers[ pCurBoard->m_userIdx ].m_localIndex );

				if ( userID.IsValid() )
				{
					pUsers[ pCurBoard->m_userIdx ].m_info |= eUIB_GPProfile;
					pUsers[ pCurBoard->m_userIdx ].m_profileId = static_cast< const SCryGameSpyUserID* >( userID.get() )->m_profileID;
				}
			}

			if ( pUsers[ pCurBoard->m_userIdx ].m_info & eUIB_GPProfile )
			{
				if ( !( pUsers[ pCurBoard->m_userIdx ].m_info & eUIB_Name ) )
				{
					if ( !pTask->m_pLink )
					{
						ECryLobbyError	error = CreateTaskLink( pTask );

						if ( error != eCLE_Success )
						{
							UpdateTaskError( sTaskID, error );
						}
					}

					if ( pTask->error == eCLE_Success )
					{
						GPResult result = GetService()->GPGetInfo( pUsers[ pCurBoard->m_userIdx ].m_profileId, GP_CHECK_CACHE, GetInfoCallback, pTask->m_pLink );

						if ( result == GP_NO_ERROR )
						{
							PassTaskLinkToGameSpy( pTask->m_pLink );
						}
						else
						{
							UpdateTaskError( sTaskID, eCLE_InternalError );
						}
					}
				}
				else
				{
					QueueNextLeaderBoardSake( sTaskID, pWriteInfo, pRequest );
				}
			}
			else
			{
				UpdateTaskError( sTaskID, eCLE_UserNotSignedIn );
			}
		}

		return false;
	}

	return true;
}

void CCryGameSpyStats::QueueNextLeaderBoardSake( CryStatsTaskID sTaskID, SCryGameSpyStatsBoardWriteInfo* pWriteInfo,SAKERequest* pRequest )
{
	STask*													pTask = &m_task[ sTaskID ];
	SUser*													pUsers = ( SUser* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_LEADERBOARD_USERS ] );
	SCryGameSpyOneLeaderBoardWrite* pCurBoard = &pWriteInfo->m_pLeaderBoards[ pWriteInfo->m_curBoard ];
	SRegisterLeaderBoardData*				pRegisteredBoard = FindRegisteredBoardInfo( pCurBoard->m_sakeCreateInput.mTableId );
	uint32													lateFieldIndex = 0;

	if ( pCurBoard->m_leaderboardType == eCLLT_Dedicated )
	{
		pCurBoard->m_sakeField[ lateFieldIndex ].mValue.mInt = pUsers[ pCurBoard->m_userIdx ].m_profileId;
		++lateFieldIndex;
	}

	pCurBoard->m_sakeField[ lateFieldIndex ].mValue.mAsciiString = pUsers[ pCurBoard->m_userIdx ].m_name;

	if ( pRegisteredBoard->m_recordIdCache.find( pUsers[ pCurBoard->m_userIdx ].m_profileId ) != pRegisteredBoard->m_recordIdCache.end() )
	{
		*pRequest = UpdateLeaderBoardRecord( pTask, pWriteInfo );
	}
	else
	{
		*pRequest = FindLeaderBoardRecord( sTaskID, pWriteInfo );
	}

	if ( !*pRequest )
	{
		UpdateTaskError( sTaskID, eCLE_InternalError );
	}
}

void CCryGameSpyStats::StartStatsWriteLeaderBoard(CryStatsTaskID sTaskID)
{
	STask* pTask = &m_task[sTaskID];

	SCryGameSpyStatsBoardWriteInfo* pWriteInfo = (SCryGameSpyStatsBoardWriteInfo*)m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_WRITE_LEADERBOARD_INFO]);
	SRegisterLeaderBoardData*				pRegisteredBoard = FindRegisteredBoardInfo(pWriteInfo->m_pLeaderBoards[pWriteInfo->m_curBoard].m_sakeCreateInput.mTableId);
	bool														done = false; 

	if (pRegisteredBoard)
	{
		done = QueueNextLeaderBoard(sTaskID,pWriteInfo,&pTask->m_sakeRequest);
	}
	else
	{
		UpdateTaskError(sTaskID, eCLE_InvalidParam);
	}

	NetLog("[Lobby] StartStatsWriteLeaderBoard result %d", pTask->error);

	if ( pTask->canceled || done || ( pTask->error != eCLE_Success ) )
	{
		StopTaskRunning(sTaskID);
	}
}

ECryLobbyError CCryGameSpyStats::StatsReadLeaderBoardByUserID(CryStatsLeaderBoardID board, CryUserID* pUserIDs, uint32 numUserIDs, CryLobbyTaskID* pTaskID, CryStatsReadLeaderBoardCallback cb, void* pCbArg)
{
	LOBBY_AUTO_LOCK;

	CryStatsTaskID sTaskID;
	SRegisterLeaderBoardData* pRegisteredBoard = NULL;
	SCryGameSpyStatsBoardReadInfo* pReadInfo = NULL;
	ECryLobbyError error = InitialiseLeaderBoardReadTask(board,sTaskID,&pRegisteredBoard,&pReadInfo,numUserIDs,pTaskID,cb,pCbArg);

	if (error == eCLE_Success)
	{
		error = CreateTaskParamMem(sTaskID, STATS_PARAM_IN_PROFILE_FILTER, NULL, (sizeof(char) * LENGTH_OF_PROFILE_AS_STRING * numUserIDs) + 15);
		if (error == eCLE_Success)
		{
			STask* pTask = &m_task[sTaskID];
			char* pFilterBuffer = (char*)m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_IN_PROFILE_FILTER]);
			
			sprintf(pFilterBuffer, "%s IN (", pReadInfo->m_sakeInput.mFieldNames[1]);
			for (uint32 a = 0; a < numUserIDs; a++)
			{
				SCryGameSpyUserID* pUserId = (SCryGameSpyUserID*)(pUserIDs[a].get());
				char entryBuffer[LENGTH_OF_PROFILE_AS_STRING];
				if (a < (numUserIDs - 1))
				{
					sprintf(entryBuffer, "%d, ", pUserId->m_profileID);
				}
				else
				{
					sprintf(entryBuffer, "%d)", pUserId->m_profileID);
				}
				strcat(pFilterBuffer, entryBuffer);
			}

			// Set up custom part of search (just the id's passed in)
			pReadInfo->m_sakeInput.mOffset = 0;
			pReadInfo->m_sakeInput.mTargetRecordFilter = pFilterBuffer;
			pReadInfo->m_sakeInput.mSurroundingRecordsCount = 0;

			FROM_GAME_TO_LOBBY(&CCryGameSpyStats::StartTaskRunning, this, sTaskID);
		}
		else
		{
			FreeTask(sTaskID);
		}
	}

	NetLog("[Lobby] Start StatsReadLeaderBoardByRankForUser return %d", error);

	return error;
}

ECryLobbyError CCryGameSpyStats::StatsReadLeaderBoardByRankForRange(CryStatsLeaderBoardID board, uint32 startRank, uint32 num, CryLobbyTaskID* pTaskID, CryStatsReadLeaderBoardCallback cb, void* pCbArg)
{
	LOBBY_AUTO_LOCK;

	CryStatsTaskID sTaskID;
	SRegisterLeaderBoardData* pRegisteredBoard = NULL;
	SCryGameSpyStatsBoardReadInfo* pReadInfo = NULL;
	ECryLobbyError error = InitialiseLeaderBoardReadTask(board,sTaskID,&pRegisteredBoard,&pReadInfo,num,pTaskID,cb,pCbArg);

	if (error == eCLE_Success)
	{
		// Set up custom part of search (just an offset from start of table - gamespy offsets are from 0)
		pReadInfo->m_sakeInput.mOffset = startRank - 1;

		FROM_GAME_TO_LOBBY(&CCryGameSpyStats::StartTaskRunning, this, sTaskID);
	}

	NetLog("[Lobby] Start StatsReadLeaderBoardByRankForUser return %d", error);

	return error;
}

ECryLobbyError CCryGameSpyStats::StatsReadLeaderBoardByRankForUser(CryStatsLeaderBoardID board, uint32 user, uint32 num, CryLobbyTaskID* pTaskID, CryStatsReadLeaderBoardCallback cb, void* pCbArg)
{
	LOBBY_AUTO_LOCK;

	CryStatsTaskID sTaskID;
	SRegisterLeaderBoardData* pRegisteredBoard = NULL;
	SCryGameSpyStatsBoardReadInfo* pReadInfo = NULL;
	ECryLobbyError error = InitialiseLeaderBoardReadTask(board,sTaskID,&pRegisteredBoard,&pReadInfo,( num + 1 ) & ( ~1 ),pTaskID,cb,pCbArg);

	if (error == eCLE_Success)
	{
		// Set up custom part of search (specific owner - myself and some surrounding results)
		pReadInfo->m_sakeInput.mOffset = 0;	
		pReadInfo->m_sakeInput.mTargetRecordFilter = pReadInfo->m_searchString;			// profileId is filled in later (because we may not have connected at this point)
		pReadInfo->m_sakeInput.mSurroundingRecordsCount = num / 2;

		FROM_GAME_TO_LOBBY(&CCryGameSpyStats::StartTaskRunning, this, sTaskID);
	}

	NetLog("[Lobby] Start StatsReadLeaderBoardByRankForUser return %d", error);

	return error;
}

ECryLobbyError CCryGameSpyStats::InitialiseLeaderBoardReadTask(CryStatsLeaderBoardID board,CryStatsTaskID &sTaskID,SRegisterLeaderBoardData** ppRegisteredBoard,SCryGameSpyStatsBoardReadInfo** ppReadInfo,uint32 num, CryLobbyTaskID* pTaskID, CryStatsReadLeaderBoardCallback cb, void* pCbArg)
{
	ECryLobbyError error = eCLE_Success;

	const char*	pOwnerName;

	switch ( m_leaderboardType )
	{
	case eCLLT_P2P:
		pOwnerName = pGameSpyFieldP2POwnerIDName;
		break;
	case eCLLT_Dedicated:
		pOwnerName = pGameSpyFieldDedicatedOwnerIDName;
		break;
	}

	*ppRegisteredBoard = FindRegisteredBoardInfo(board, m_leaderboardType);

	if (*ppRegisteredBoard != NULL)
	{
		ECryLobbyError error = StartTask(eT_StatsReadLeaderBoard, false, 0, &sTaskID, pTaskID, CrySessionInvalidHandle, (void*)cb, pCbArg);

		if (error == eCLE_Success)
		{
			STask* pTask = &m_task[sTaskID];

			pTask->m_leaderboardType = m_leaderboardType;

			error = CreateTaskParamMem(sTaskID, STATS_PARAM_READ_LEADERBOARD_ENTRY, NULL, sizeof(SCryGameSpyStatsBoardReadInfo));
			if (error == eCLE_Success)
			{
				error = CreateTaskParamMem(sTaskID, STATS_PARAM_OUT_LEADERBOARD_RESULT, NULL, sizeof(SCryStatsLeaderBoardReadResult));
				if (error == eCLE_Success)
				{
					error = CreateTaskParamMem(sTaskID, STATS_PARAM_OUT_LEADERBOARD_ROWS, NULL, sizeof(SCryStatsLeaderBoardReadRow) * num);
					if (error == eCLE_Success)
					{
						error = CreateTaskParamMem(sTaskID, STATS_PARAM_OUT_LEADERBOARD_COLUMNS, NULL, sizeof(SCryStatsLeaderBoardUserColumn) * num * (*ppRegisteredBoard)->m_numColumns);

						if (error == eCLE_Success)
						{
							int a;
							*ppReadInfo = (SCryGameSpyStatsBoardReadInfo*)m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_READ_LEADERBOARD_ENTRY]);
							memset(*ppReadInfo, 0, sizeof(SCryGameSpyStatsBoardReadInfo));

							(*ppReadInfo)->m_sakeInput.mFilter = NULL;
							(*ppReadInfo)->m_sakeInput.mTableId = const_cast<char *>((*ppRegisteredBoard)->m_tableID.c_str());
							(*ppReadInfo)->m_sakeInput.mFieldNames = (*ppReadInfo)->m_pFieldNames;
							(*ppReadInfo)->m_sakeInput.mFieldNames[0]=const_cast<char *>(pGameSpyFieldRowName);
							(*ppReadInfo)->m_sakeInput.mFieldNames[1]=const_cast<char *>(pOwnerName);
							(*ppReadInfo)->m_sakeInput.mFieldNames[2]=const_cast<char *>(pGameSpyFieldOwnerNameName);
							(*ppReadInfo)->m_sakeInput.mFieldNames[3]=const_cast<char *>(pGameSpyFieldScoreName);
							(*ppReadInfo)->m_sakeInput.mFieldNames[4]=const_cast<char *>(pGameSpyFieldRecordName);

							for (a=0;a<(*ppRegisteredBoard)->m_numColumns;a++)
							{
								(*ppReadInfo)->m_sakeInput.mFieldNames[GAMESPY_RESERVED_FIELDS+a]=(char*)((*ppRegisteredBoard)->m_pColumns[a].m_fieldID.c_str());
							}

							(*ppReadInfo)->m_sakeInput.mNumFields = GAMESPY_RESERVED_FIELDS+((*ppRegisteredBoard)->m_numColumns);
							(*ppReadInfo)->m_sakeInput.mSort = const_cast<char *>(pGameSpyTableOrder);
							(*ppReadInfo)->m_sakeInput.mMaxRecords = num;
							(*ppReadInfo)->m_numRecordsRequested = num;
						}
					}
				}
			}

			if (error != eCLE_Success)
			{
				FreeTask(sTaskID);
			}
		}
	}
	else
	{
		error = eCLE_InvalidParam;
	}

	return error;
}


void CCryGameSpyStats::GetInfoCallback(GPConnection* pConnection, void* pArg, void* pParam)
{
	STaskLink*											pLink = static_cast< STaskLink* >( pParam );

	if ( pLink )
	{
		CCryGameSpyStats*								pThis = pLink->pStats;

		if ( pThis )
		{
			STask*													pTask = pLink->pTask;

			if ( pTask )
			{
				CryStatsTaskID									sTaskID = CryStatsTaskID(pTask - pThis->m_task);
				GPGetInfoResponseArg*						pGetInfoResponseArg = static_cast< GPGetInfoResponseArg* >( pArg );
				GPResult												result;

				if ( pGetInfoResponseArg )
				{
					result = pGetInfoResponseArg->result;
				}
				else
				{
					result = GP_MISC_ERROR;
				}

				if ( result == GP_NO_ERROR )
				{
					SUser*													pUsers = ( SUser* )pThis->m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_LEADERBOARD_USERS ] );
					SCryGameSpyStatsBoardWriteInfo* pWriteInfo = ( SCryGameSpyStatsBoardWriteInfo* )pThis->m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_LEADERBOARD_INFO ] );
					SCryGameSpyOneLeaderBoardWrite* pCurBoard = &pWriteInfo->m_pLeaderBoards[ pWriteInfo->m_curBoard ];
					
					strcpy_s( pUsers[ pCurBoard->m_userIdx ].m_name, GP_UNIQUENICK_LEN, pGetInfoResponseArg->uniquenick );
					pUsers[ pCurBoard->m_userIdx ].m_info |= eUIB_Name;
					pThis->QueueNextLeaderBoardSake( sTaskID, pWriteInfo, &pTask->m_sakeRequest );
				}
				else
				{
					pThis->UpdateTaskError( sTaskID, eCLE_InternalError );
				}

				if ( pTask->error != eCLE_Success )
				{
					pThis->StopTaskRunning( sTaskID );
				}
			}

			pThis->FreeTaskLinkFromGameSpy( pLink );
		}
	}
}

void CCryGameSpyStats::GetRecordCountCallback(SAKE sake, SAKERequest request, SAKERequestResult result, void* pInputData, void* pOutputData, void* pUserData)
{
	CCryGameSpyStats* _this = static_cast<CCryGameSpyStats *>(pUserData);
	SAKEGetRecordCountInput* pInput = (SAKEGetRecordCountInput *)pInputData;
	SAKEGetRecordCountOutput* pOutput = (SAKEGetRecordCountOutput *)pOutputData;

	CryStatsTaskID taskId = _this->FindTaskFromRequest(request);
	if (taskId != CryStatsInvalidTaskID)
	{
		STask* pTask = &_this->m_task[taskId];

		if (result == SAKERequestResult_SUCCESS)
		{
			SCryStatsLeaderBoardReadResult* pResult = (SCryStatsLeaderBoardReadResult*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_OUT_LEADERBOARD_RESULT]);
			SCryGameSpyStatsBoardReadInfo*	pReadInfo = (SCryGameSpyStatsBoardReadInfo*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_READ_LEADERBOARD_ENTRY]);
			SRegisterLeaderBoardData*				pRegisteredBoard = _this->FindRegisteredBoardInfo( pInput->mTableId );

			pRegisteredBoard->m_recordCount = pOutput->mCount;
			pResult->totalNumBoardRows = pOutput->mCount;
		}
		else
		{
			_this->UpdateTaskError(taskId,eCLE_InternalError);
		}

		_this->StopTaskRunning( taskId );
	}
}

void CCryGameSpyStats::SearchForRecordsCallback(SAKE sake, SAKERequest request, SAKERequestResult result, void* pInputData, void* pOutputData, void* pUserData)
{
	CCryGameSpyStats* _this = static_cast<CCryGameSpyStats *>(pUserData);
	SAKESearchForRecordsInput* pInput = (SAKESearchForRecordsInput *)pInputData;
	SAKESearchForRecordsOutput* pOutput = (SAKESearchForRecordsOutput *)pOutputData;

	CryStatsTaskID taskId = _this->FindTaskFromRequest(request);
	if (taskId != CryStatsInvalidTaskID)
	{
		STask* pTask = &_this->m_task[taskId];

		const char* pOwnerName;

		switch ( pTask->m_leaderboardType )
		{
		case eCLLT_P2P:
			pOwnerName = pGameSpyFieldP2POwnerIDName;
			break;
		case eCLLT_Dedicated:
			pOwnerName = pGameSpyFieldDedicatedOwnerIDName;
			break;
		}

		if (result == SAKERequestResult_SUCCESS)
		{
			SCryStatsLeaderBoardReadResult* pResult = (SCryStatsLeaderBoardReadResult*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_OUT_LEADERBOARD_RESULT]);
			if (pResult)
			{
				SCryStatsLeaderBoardReadRow* pRows = (SCryStatsLeaderBoardReadRow*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_OUT_LEADERBOARD_ROWS]);

				if (pRows)
				{
					SCryStatsLeaderBoardUserColumn* pColumns = (SCryStatsLeaderBoardUserColumn*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_OUT_LEADERBOARD_COLUMNS]);
					int colIndex=0;

					if (pColumns)
					{
						SRegisterLeaderBoardData* pRegisteredBoard = _this->FindRegisteredBoardInfo(pInput->mTableId);

						if (pRegisteredBoard)
						{
							SCryGameSpyStatsBoardReadInfo*	pReadInfo = (SCryGameSpyStatsBoardReadInfo*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_READ_LEADERBOARD_ENTRY]);
							uint32													highestRank = 0;
							uint32													offset = 0;
							uint32													numRecords = pOutput->mNumRecords;

							if ( pInput->mTargetRecordFilter )
							{
								if ( numRecords > pReadInfo->m_numRecordsRequested )
								{
									// GameSpy's target record + N surrounding records search forced us to
									// ask for an odd number of records but we really wanted an even number.
									// Drop the end record furthest from the target record to move the target
									// record closer to the centre.

									uint32		i;

									for ( i = 0; i < numRecords; ++i )
									{
										SAKEField* pOwnerField = _this->FindFieldFromName( pOutput->mRecords[ i ], pInput->mNumFields, pOwnerName );

										if ( pOwnerField )
										{
											if ( pOwnerField->mValue.mInt == _this->m_profileId )
											{
												break;
											}
										}
									}

									if ( i > numRecords - i - 1 )
									{
										++offset;
									}

									--numRecords;
								}
							}

							pResult->id = pRegisteredBoard->m_boardID;
							pResult->numRows = numRecords;
							pResult->pRows = pRows;

							for ( int a = 0; a < numRecords; ++a )
							{
								SAKEField* pRecordField = _this->FindFieldFromName( pOutput->mRecords[ a + offset ], pInput->mNumFields, pGameSpyFieldRecordName );
								SAKEField* pOwnerField = _this->FindFieldFromName( pOutput->mRecords[ a + offset ], pInput->mNumFields, pOwnerName );
								SAKEField* pNameField = _this->FindFieldFromName( pOutput->mRecords[ a + offset ], pInput->mNumFields, pGameSpyFieldOwnerNameName );
								SAKEField* pRowField = _this->FindFieldFromName( pOutput->mRecords[ a + offset ], pInput->mNumFields, pGameSpyFieldRowName );

								if (pRecordField && pOwnerField)
								{
									pRegisteredBoard->m_recordIdCache[pOwnerField->mValue.mInt] = pRecordField->mValue.mInt;
								}

								if ( pRowField )
								{
									pRows[ a ].rank = pRowField->mValue.mInt;

									if ( pRows[ a ].rank > highestRank )
									{
										highestRank = pRows[ a ].rank;
									}
								}
								else
								{
									pRows[a].rank = 0;
								}

								new ( &pRows[a].userID ) CryUserID();

								if (pOwnerField)
								{
									SCryGameSpyUserID* pNewUserId = new SCryGameSpyUserID(pOwnerField->mValue.mInt);
									pRows[a].userID = pNewUserId;
								}
								else
								{
									pRows[a].userID = CryUserInvalidID;
								}

								if ( pNameField && pNameField->mValue.mAsciiString[ 0 ] )
								{
									strcpy_s( pRows[a].name, sizeof ( pRows[a].name ), pNameField->mValue.mAsciiString );
								}
								else
								{
									strcpy_s( pRows[a].name, sizeof ( pRows[a].name ), "Unknown" );
								}

								SAKEField* pScoreField = _this->FindFieldFromName(pOutput->mRecords[ a + offset ],pInput->mNumFields,pGameSpyFieldScoreName);

								pRows[a].data.score.id = 0;			// check logic on this one
								if (pScoreField)
								{
									pRows[a].data.score.score = pScoreField->mValue.mInt64;
								}
								else
								{
									pRows[a].data.score.score = 0;
								}

								/// for each column now.
								pRows[a].data.pColumns = &pColumns[colIndex];
								pRows[a].data.numColumns = pRegisteredBoard->m_numColumns;
								for (int b=0;b<pRegisteredBoard->m_numColumns;b++)
								{
									pColumns[colIndex].columnID = pRegisteredBoard->m_pColumns[b].m_columnID;

									SAKEField* pSakeColumn = _this->FindFieldFromName(pOutput->mRecords[ a + offset ],pInput->mNumFields,pRegisteredBoard->m_pColumns[b].m_fieldID.c_str());

									pColumns[colIndex].data.m_id = pRegisteredBoard->m_pColumns[b].m_dataID;
									pColumns[colIndex].data.m_type = pRegisteredBoard->m_pColumns[b].m_dataType;
									if (pSakeColumn)
									{
										pColumns[colIndex].data.m_int64 = pSakeColumn->mValue.mInt64;
									}
									else
									{
										pColumns[colIndex].data.m_int64 = 0;
									}

									colIndex++;
								}
							}

							if ( highestRank >= pRegisteredBoard->m_recordCount )
							{
								pReadInfo->m_recCountInput.mTableId = pReadInfo->m_sakeInput.mTableId;
								pReadInfo->m_recCountInput.mFilter = const_cast<char *>(pGameSpyRecordFilter);
								pTask->m_sakeRequest = sakeGetRecordCount(sake, &pReadInfo->m_recCountInput,GetRecordCountCallback,_this);

								if (pTask->m_sakeRequest == NULL)
								{
									_this->UpdateTaskError(taskId,eCLE_InternalError);
								}
							}
							else
							{
								pResult->totalNumBoardRows = pRegisteredBoard->m_recordCount;
								_this->StopTaskRunning( taskId );
							}
						}
						else
						{
							_this->UpdateTaskError(taskId,eCLE_InternalError);
						}
					}
					else
					{
						_this->UpdateTaskError(taskId,eCLE_InternalError);
					}
				}
				else
				{
					_this->UpdateTaskError(taskId,eCLE_InternalError);
				}
			}
			else
			{
				_this->UpdateTaskError(taskId,eCLE_InternalError);
			}
		}
		else
		{
			_this->UpdateTaskError(taskId,eCLE_InternalError);
		}

		if (pTask->error != eCLE_Success)
		{
			((CryStatsReadLeaderBoardCallback)pTask->pCb)(pTask->lTaskID, pTask->error, NULL, pTask->pCbArg);
			_this->StopTaskRunning(taskId);
		}
	}
}

void CCryGameSpyStats::ReadUserDataCallback(SAKE sake, SAKERequest request, SAKERequestResult result, void* pInputData, void* pOutputData, void* pUserData)
{
	CCryGameSpyStats* _this = static_cast<CCryGameSpyStats *>(pUserData);
	CryStatsTaskID taskId = _this->FindTaskFromRequest(request);
	if (taskId != CryStatsInvalidTaskID)
	{
		STask* pTask = &_this->m_task[taskId];
		SCryGameSpyStatsUserReadInfo*	pReadInfo = (SCryGameSpyStatsUserReadInfo*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_READ_USERDATA_ENTRY]);

		if (result == SAKERequestResult_SUCCESS)
		{
			SCryLobbyUserData* pResults = (SCryLobbyUserData*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_READ_USERDATA_RESULTS]);
			SAKEField* pRecordField = NULL;
			SAKEField* pRowField = NULL;

			if ( pTask->m_leaderboardType == eCLLT_P2P )
			{
				SAKEGetMyRecordsInput* pInput = (SAKEGetMyRecordsInput *)pInputData;
				SAKEGetMyRecordsOutput* pOutput = (SAKEGetMyRecordsOutput *)pOutputData;

				if (pOutput->mNumRecords==1)				// We will either get 1 entry back or 0. If 0 fire callback with error stating no user data written yet
				{
					pRecordField = _this->FindFieldFromName(pOutput->mRecords[0],pInput->mNumFields,pGameSpyFieldRecordName);
					pRowField = _this->FindFieldFromName(pOutput->mRecords[0],pInput->mNumFields,pGameSpyStatsFieldName);
				}
				else
				{
					_this->UpdateTaskError(taskId,eCLE_ReadDataNotWritten);
				}
			}
			else
			{
				SAKESearchForRecordsInput* pInput = (SAKESearchForRecordsInput *)pInputData;
				SAKESearchForRecordsOutput* pOutput = (SAKESearchForRecordsOutput *)pOutputData;

				if (pOutput->mNumRecords==1)				// We will either get 1 entry back or 0. If 0 fire callback with error stating no user data written yet
				{
					pRecordField = _this->FindFieldFromName(pOutput->mRecords[0],pInput->mNumFields,pGameSpyFieldRecordName);
					pRowField = _this->FindFieldFromName(pOutput->mRecords[0],pInput->mNumFields,pGameSpyStatsFieldName);
				}
				else
				{
					_this->UpdateTaskError(taskId,eCLE_ReadDataNotWritten);
				}
			}

			if (pRecordField)
			{
				_this->m_statsRecordIdCache[ pTask->m_leaderboardType ][ pReadInfo->m_profileId ] = pRecordField->mValue.mInt;
			}

			if (pRowField)
			{
				if (pRowField->mValue.mBinaryData.mLength == MAX_GAMESPY_PROFILE_SIZE)
				{
					const uint8* pBuffer = (uint8*)pRowField->mValue.mBinaryData.mValue;
					uint8* pUnencryptedBuffer = (uint8*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_READ_USERDATA_DECRYPT_BUFFER]);

					memset(pUnencryptedBuffer, 0, MAX_GAMESPY_PROFILE_SETTING_BUFFER_SIZE);

					_this->UpdateTaskError(taskId, _this->DecryptUserData(MAX_GAMESPY_PROFILE_SETTING_BUFFER_SIZE, pBuffer, pUnencryptedBuffer));

					if (pTask->error == eCLE_Success)
					{
						CCrySharedLobbyPacket packet;

						packet.SetReadBuffer(pUnencryptedBuffer, MAX_GAMESPY_PROFILE_SETTING_BUFFER_SIZE);
						packet.SetReadBufferPos(0);

						for (uint32 j = 0; j < _this->m_numUserData; j++)
						{
							SCryLobbyUserData* pResult = &pResults[j];
							SRegisterUserData* pRegistered = &_this->m_userData[j];

							pResult->m_id = pRegistered->m_id;
							pResult->m_type = (ECryLobbyUserDataType)pRegistered->m_type;
							packet.ReadCryLobbyUserData(pResult);
						}
					}
				}
				else
				{
					_this->UpdateTaskError(taskId,eCLE_ReadDataCorrupt);
				}
			}
			else
			{
				_this->UpdateTaskError(taskId,eCLE_InternalError);
			}
		}
		else
		{
			_this->UpdateTaskError(taskId,eCLE_InternalError);
		}

		NetLog("[Lobby] ReadUserDataCallback error %d sakeRequestResult %d", pTask->error, result);

		_this->StopTaskRunning(taskId);
	}
}

SAKEField* CCryGameSpyStats::FindFieldFromName(SAKEField* pFields,int numFields,const char* pName)
{
	int a;
	for (a=0;a<numFields;a++)
	{
		if (strcmp(pFields[a].mName,pName)==0)
		{
			return &pFields[a];
		}
	}

	return NULL;
}

void CCryGameSpyStats::ResetRecordData()
{
	int a;
	int b;

	for (a=0;a<eCLLT_Num;a++)
	{
		m_statsRecordIdCache[a].clear();

		for (b=0;b<m_numLeaderBoards;++b)
		{
			m_leaderBoards[b].m_boards[a].m_recordIdCache.clear();
		}
	}
}

CCryGameSpyStats::SRegisterLeaderBoardData* CCryGameSpyStats::FindRegisteredBoardInfo(CryStatsLeaderBoardID boardId, ECryLobbyLeaderboardType type)
{
	int a;

	for (a=0;a<m_numLeaderBoards;a++)
	{
		if (m_leaderBoards[a].m_boards[type].m_boardID == boardId)
		{
			return &m_leaderBoards[a].m_boards[type];
		}
	}
	return NULL;
}

CCryGameSpyStats::SRegisterLeaderBoardData* CCryGameSpyStats::FindRegisteredBoardInfo(const char* pTableId)
{
	int a;

	for (a=0;a<m_numLeaderBoards;a++)
	{
		for ( uint32 i = 0; i < eCLLT_Num; ++i )
		{
			if (m_leaderBoards[a].m_boards[i].m_tableID == pTableId)
			{
				return &m_leaderBoards[a].m_boards[i];
			}
		}
	}
	return NULL;
}

CCryGameSpyStats::SRegisterLeaderBoardData::SColumn* CCryGameSpyStats::FindColumnFromID(SRegisterLeaderBoardData* pTable,CryLobbyUserDataID id)
{
	int a;

	for (a=0;a<pTable->m_numColumns;a++)
	{
		if (pTable->m_pColumns[a].m_dataID == id)
		{
			return &pTable->m_pColumns[a];
		}
	}
	return NULL;
}

CryStatsTaskID CCryGameSpyStats::FindTaskFromRequest(SAKERequest request)
{
	int a;

	for (a=0;a<MAX_STATS_TASKS;a++)
	{
		if (m_task[a].used && m_task[a].running && (m_task[a].m_sakeRequest == request))
		{
			return a;
		}
	}

	return CryStatsInvalidTaskID;
}

void CCryGameSpyStats::StartStatsReadLeaderBoard(CryStatsTaskID sTaskID)
{
	STask* pTask = &m_task[sTaskID];

	const char*	pSearchFormat;

	switch ( pTask->m_leaderboardType )
	{
	case eCLLT_P2P:
		pSearchFormat = pGameSpySearchP2PFormat;
		break;
	case eCLLT_Dedicated:
		pSearchFormat = pGameSpySearchDedicatedFormat;
		break;
	}

	SCryGameSpyStatsBoardReadInfo* pReadInfo = (SCryGameSpyStatsBoardReadInfo*)m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_READ_LEADERBOARD_ENTRY]);
	
	if (pReadInfo->m_sakeInput.mTargetRecordFilter)
	{
		sprintf(pReadInfo->m_searchString,pSearchFormat,m_profileId);
	}

	if ( !( pReadInfo->m_sakeInput.mFilter || pReadInfo->m_sakeInput.mTargetRecordFilter ) )
	{
		if ( pReadInfo->m_sakeInput.mOffset < GAMESPY_LEADERBOARD_TOP_CACHE )
		{
			pReadInfo->m_sakeInput.mCacheFlag = gsi_true;
		}
	}

	pTask->m_sakeRequest = sakeSearchForRecords(m_sake,&pReadInfo->m_sakeInput,SearchForRecordsCallback,this);

	if (pTask->m_sakeRequest == NULL)
	{
		UpdateTaskError(sTaskID, eCLE_InternalError);
	}

	NetLog("[Lobby] StartStatsReadLeaderBoardByRankForUser result %d", pTask->error);

	if (pTask->canceled || pTask->error != eCLE_Success)
	{
		StopTaskRunning(sTaskID);
	}
}

void CCryGameSpyStats::EndStatsReadLeaderBoard(CryStatsTaskID sTaskID)
{
	STask* pTask = &m_task[sTaskID];
	SCryStatsLeaderBoardReadResult* pResult = (SCryStatsLeaderBoardReadResult*)m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_OUT_LEADERBOARD_RESULT]);

	((CryStatsReadLeaderBoardCallback)pTask->pCb)(pTask->lTaskID, pTask->error, pResult, pTask->pCbArg);
}

ECryLobbyError CCryGameSpyStats::StatsRegisterUserData(SCryLobbyUserData* pData, uint32 numData, CryLobbyTaskID* pTaskID, CryStatsCallback cb, void* pCbArg)
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError error = eCLE_Success;
	CryStatsTaskID sTaskID;
	
	m_nCumulativeUserDataSize = 0;
	m_numUserData = 0;

	error = StartTask(eT_StatsRegisterUserData, false, 0, &sTaskID, pTaskID, CrySessionInvalidHandle, (void*)cb, pCbArg);

	if (error == eCLE_Success)
	{
		for ( int i = 0; i < eCLLT_Num; ++i )
		{
			m_statsRecordIdCache[ i ].clear();
		}

		if ((m_numUserData + numData) > m_userData.size())
		{
			m_userData.resize(m_userData.size() + numData);
		}

		for (uint32 i = 0; (i < numData) && (error == eCLE_Success); i++)
		{
			uint32 itemSize;

			switch (pData[i].m_type)
			{
				case eCLUDT_Int64:
					itemSize = sizeof(pData[i].m_int64);
				break;

				case eCLUDT_Int32:
					itemSize = sizeof(pData[i].m_int32);
				break;

				case eCLUDT_Int16:
					itemSize = sizeof(pData[i].m_int16);
				break;

				case eCLUDT_Int8:
					itemSize = sizeof(pData[i].m_int8);
				break;

				case eCLUDT_Float64:
					itemSize = sizeof(pData[i].m_f64);
				break;

				case eCLUDT_Float32:
					itemSize = sizeof(pData[i].m_f32);
				break;

				case eCLUDT_Int64NoEndianSwap:
					itemSize = sizeof(pData[i].m_int64);
				break;

				default:
					itemSize = 0;
					CRY_ASSERT_MESSAGE(0, "CCryGameSpyStats::StatsRegisterUserData: Undefined data type");
				break;
			}

			if ((m_nCumulativeUserDataSize + itemSize) <= MAX_GAMESPY_PROFILE_SETTING_BUFFER_SIZE)
			{
				m_userData[m_numUserData].m_id = pData[i].m_id;
				m_userData[m_numUserData].m_type = pData[i].m_type;
				m_numUserData++;
				m_nCumulativeUserDataSize += itemSize;
			}
			else
			{
				CRY_ASSERT_MESSAGE(0, "CCryGameSpyStats::StatsRegisterUserData: To much data to fit into the allocated TUS size.");
				error = eCLE_OutOfUserData;
			}
		}

		FROM_GAME_TO_LOBBY(&CCryGameSpyStats::StartTaskRunning, this, sTaskID);
	}

	NetLog("[Lobby] Start StatsRegisterUserData return %d", error);

	return error;
}

ECryLobbyError CCryGameSpyStats::StatsWriteUserData(uint32 user, SCryLobbyUserData* pData, uint32 numData, CryLobbyTaskID* pTaskID, CryStatsCallback cb, void* pCbArg)
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError error = eCLE_Success;

	if (m_numUserData > 0)
	{
		CryStatsTaskID sTaskID;

		error = StartTask( eT_StatsWriteUserData, false, 0, &sTaskID, pTaskID, CrySessionInvalidHandle, ( void* )cb, pCbArg );

		if ( error == eCLE_Success )
		{
			error = CreateTaskParamMem( sTaskID, STATS_PARAM_WRITE_USERDATA_USERS, NULL, sizeof ( SUser ) );

			if ( error == eCLE_Success )
			{
				SUser*	pUsers = ( SUser* )m_pLobby->MemGetPtr( m_task[ sTaskID ].paramsMem[ STATS_PARAM_WRITE_USERDATA_USERS ] );
				uint32	userIdx = 0;

				pUsers->m_info = eUIB_LocalIndex;
				pUsers->m_localIndex = user;
				error = StatsWriteUserData( sTaskID, &pData, &numData, 1 );
			}

			if ( error != eCLE_Success )
			{
				FreeTask( sTaskID );
			}
		}
	}
	else
	{
		error = eCLE_NoUserDataRegistered;
	}

	return error;
}

ECryLobbyError CCryGameSpyStats::StatsWriteUserData(CryUserID* pUserIDs, SCryLobbyUserData** ppData, uint32* pNumData, uint32 numUserIDs, CryLobbyTaskID* pTaskID, CryStatsCallback cb, void* pCbArg)
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError error = eCLE_Success;

	if (m_numUserData > 0)
	{
		CryStatsTaskID sTaskID;

		error = StartTask( eT_StatsWriteUserData, false, 0, &sTaskID, pTaskID, CrySessionInvalidHandle, ( void* )cb, pCbArg );

		if ( error == eCLE_Success )
		{
			error = CreateTaskParamMem( sTaskID, STATS_PARAM_WRITE_USERDATA_USERS, NULL, numUserIDs * sizeof ( SUser ) );

			if ( error == eCLE_Success )
			{
				SUser*	pUsers = ( SUser* )m_pLobby->MemGetPtr( m_task[ sTaskID ].paramsMem[ STATS_PARAM_WRITE_USERDATA_USERS ] );

				for ( uint32 i = 0; i < numUserIDs; ++i )
				{
					pUsers[ i ].m_info = eUIB_GPProfile;
					pUsers[ i ].m_profileId = static_cast< const SCryGameSpyUserID* >( pUserIDs[ i ].get() )->m_profileID;
				}

				error = StatsWriteUserData( sTaskID, ppData, pNumData, numUserIDs );
			}

			if ( error != eCLE_Success )
			{
				FreeTask( sTaskID );
			}
		}
	}
	else
	{
		error = eCLE_NoUserDataRegistered;
	}

	return error;
}

ECryLobbyError CCryGameSpyStats::StatsWriteUserData( CryStatsTaskID sTaskID, SCryLobbyUserData** ppData, uint32* pNumData, uint32 numUsers )
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError error = eCLE_Success;

	STask*	pTask = &m_task[ sTaskID ];

	if ( gEnv->IsDedicated() )
	{
		pTask->m_leaderboardType = eCLLT_Dedicated;
	}
	else
	{
		pTask->m_leaderboardType = eCLLT_P2P;
	}

	error = CreateTaskParamMem(sTaskID, STATS_PARAM_WRITE_USERDATA_INFO, NULL, sizeof( SCryGameSpyStatsUserDataWriteInfo ) );

	if (error == eCLE_Success)
	{
		error = CreateTaskParamMem(sTaskID, STATS_PARAM_WRITE_USERDATA_ENCRYPT_BUFFER, NULL, MAX_GAMESPY_PROFILE_SETTING_BUFFER_SIZE);
		if (error == eCLE_Success)
		{
			SCryGameSpyStatsUserDataWriteInfo* pWriteInfo = ( SCryGameSpyStatsUserDataWriteInfo* )m_pLobby->MemGetPtr( m_task[ sTaskID ].paramsMem[ STATS_PARAM_WRITE_USERDATA_INFO ] );

			pWriteInfo->m_numUserData = numUsers;
			pWriteInfo->m_curUserData = 0;
			error = BuildStatsWriteArray( sTaskID, numUsers, 0, ppData, pNumData, &pWriteInfo->m_pUserData, &pWriteInfo->m_pBuf, 0 );

			if ( error == eCLE_Success )
			{
				FROM_GAME_TO_LOBBY( &CCryGameSpyStats::StartTaskRunning, this, sTaskID );
			}
		}
	}

	NetLog("[Lobby] Stats StatsWriteUserData return %d", error);

	return error;
}

ECryLobbyError CCryGameSpyStats::BuildStatsWriteArray( CryStatsTaskID sTaskID, uint32 numUsers, uint32 userIdx, SCryLobbyUserData** ppData, uint32* pNumData, SCryGameSpyOneUserDataWrite** ppWrites, uint8** ppBuf, uint32 bufLen )
{
	ECryLobbyError	error = eCLE_Success;
	STask*					pTask = &m_task[ sTaskID ];

	if ( userIdx == numUsers )
	{
		error = CreateTaskParamMem( sTaskID, STATS_PARAM_WRITE_USERDATA_WRITES, NULL, numUsers * sizeof ( SCryGameSpyOneUserDataWrite ) );

		if ( error == eCLE_Success )
		{
			error = CreateTaskParamMem( sTaskID, STATS_PARAM_WRITE_USERDATA_DATA, NULL, bufLen );

			if ( error == eCLE_Success )
			{
				SCryGameSpyOneUserDataWrite*	pWrites = ( SCryGameSpyOneUserDataWrite* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_WRITES ] );
				uint8*												pBuf = ( uint8* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_DATA ] );

				*ppWrites = &pWrites[ numUsers ];
				*ppBuf = &pBuf[ bufLen ];
			}
		}
	}
	else
	{
		SCryLobbyUserData*					pInUserData = NULL;
		SRegisterUserData*					pRegisteredUserData = NULL;
		uint32											dataSize = 0;

		if ( pNumData[ userIdx ] != m_numUserData )
		{
			error = eCLE_UserDataMissMatch;
		}
		else
		{
			for ( uint32 i = 0; i < pNumData[ userIdx ]; ++i )
			{
				if ( ( ppData[ userIdx ][ i ].m_id != m_userData[ i ].m_id ) || ( ppData[ userIdx ][ i ].m_type != m_userData[ i ].m_type ) )
				{
					error = eCLE_UserDataMissMatch;
					break;
				}
			}

			dataSize = MAX_GAMESPY_PROFILE_SETTING_BUFFER_SIZE;
		}

		if ( error == eCLE_Success )
		{
			error = BuildStatsWriteArray( sTaskID, numUsers, userIdx + 1, ppData, pNumData, ppWrites, ppBuf, bufLen + dataSize );

			if ( error == eCLE_Success )
			{
				--*ppWrites;
				*ppBuf -= dataSize;

				( *ppWrites )->m_leaderboardType = pTask->m_leaderboardType;
				( *ppWrites )->m_userIdx = userIdx;
				( *ppWrites )->m_pData = *ppBuf;

				uint8* pEncryptBuffer = (uint8*)m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_ENCRYPT_BUFFER ] );
				memset(pEncryptBuffer, 0, MAX_GAMESPY_PROFILE_SETTING_BUFFER_SIZE);

				CCrySharedLobbyPacket packet;
				packet.SetWriteBuffer(pEncryptBuffer, dataSize);
				packet.SetWriteBufferPos( 0 );

				for ( uint32 i = 0; i < m_numUserData; ++i )
				{
					packet.WriteCryLobbyUserData( &ppData[ userIdx ][ i ] );
				}

				memset(*ppBuf, 0, MAX_GAMESPY_PROFILE_SETTING_BUFFER_SIZE);

				error = EncryptUserData(MAX_GAMESPY_PROFILE_SETTING_BUFFER_SIZE, pEncryptBuffer, *ppBuf);
			}
		}
	}

	return error;
}

void CCryGameSpyStats::StatsGetCallback(SAKE sake, SAKERequest request, SAKERequestResult result, void* pInputData, void* pOutputData, void* pUserData)
{
	CCryGameSpyStats*						_this = static_cast< CCryGameSpyStats* >( pUserData );

	CryStatsTaskID taskId = _this->FindTaskFromRequest(request);
	if (taskId != CryStatsInvalidTaskID)
	{
		STask* pTask = &_this->m_task[taskId];

		pTask->m_sakeRequest = NULL;

		if (result == SAKERequestResult_SUCCESS)
		{
			SUser*															pUsers = ( SUser* )_this->m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_USERS ] );
			SCryGameSpyStatsUserDataWriteInfo*	pWriteInfo = ( SCryGameSpyStatsUserDataWriteInfo* )_this->m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_INFO ] );
			SCryGameSpyOneUserDataWrite*				pWrite = &pWriteInfo->m_pUserData[ pWriteInfo->m_curUserData ];
			SAKEField*													pRecordField = NULL;

			if ( pWrite->m_leaderboardType == eCLLT_P2P )
			{
				SAKEGetMyRecordsInput*	pInput = static_cast< SAKEGetMyRecordsInput* >( pInputData );
				SAKEGetMyRecordsOutput*	pOutput = static_cast< SAKEGetMyRecordsOutput* >( pOutputData );

				if (pOutput->mNumRecords==1)				// We have a record, retrieve its id and kick off an update
				{
					pRecordField = _this->FindFieldFromName(pOutput->mRecords[0],pInput->mNumFields,pGameSpyFieldRecordName);
				}
			}
			else
			{
				SAKESearchForRecordsInput*	pInput = static_cast< SAKESearchForRecordsInput* >( pInputData );
				SAKESearchForRecordsOutput*	pOutput = static_cast< SAKESearchForRecordsOutput* >( pOutputData );

				if (pOutput->mNumRecords==1)				// We have a record, retrieve its id and kick off an update
				{
					pRecordField = _this->FindFieldFromName(pOutput->mRecords[0],pInput->mNumFields,pGameSpyFieldRecordName);
				}
			}

			if (pRecordField)
			{
				_this->m_statsRecordIdCache[ pWrite->m_leaderboardType ][ pUsers[ pWrite->m_userIdx ].m_profileId ] = pRecordField->mValue.mInt;
			}

			if ( _this->m_statsRecordIdCache[ pWrite->m_leaderboardType ].find( pUsers[ pWrite->m_userIdx ].m_profileId ) != _this->m_statsRecordIdCache[ pWrite->m_leaderboardType ].end() )
			{
				pTask->m_sakeRequest = _this->UpdateStatsRecord(taskId, pWrite);
			}
			else
			{
				pTask->m_sakeRequest = _this->CreateStatsRecord(taskId, pWrite);
			}
		}

		if (pTask->m_sakeRequest == NULL)
		{
			_this->UpdateTaskError(taskId,eCLE_InternalError);
		}

		if (pTask->error != eCLE_Success)
		{
			_this->StopTaskRunning(taskId);
		}
	}
}

void CCryGameSpyStats::StatsCreateCallback(SAKE sake, SAKERequest request, SAKERequestResult result, void* pInputData, void* pOutputData, void* pUserData)
{
	CCryGameSpyStats* _this = static_cast<CCryGameSpyStats *>(pUserData);
	SAKECreateRecordInput* pInput = (SAKECreateRecordInput *)pInputData;
	SAKECreateRecordOutput* pOutput= (SAKECreateRecordOutput *)pOutputData;

	CryStatsTaskID taskId = _this->FindTaskFromRequest(request);
	if (taskId != CryStatsInvalidTaskID)
	{
		STask*															pTask = &_this->m_task[taskId];
		SUser*															pUsers = ( SUser* )_this->m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_USERS ] );
		SCryGameSpyStatsUserDataWriteInfo*	pWriteInfo = ( SCryGameSpyStatsUserDataWriteInfo* )_this->m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_INFO ] );
		SCryGameSpyOneUserDataWrite*				pWrite = &pWriteInfo->m_pUserData[ pWriteInfo->m_curUserData ];

		if (result == SAKERequestResult_SUCCESS)
		{
			_this->m_statsRecordIdCache[ pWrite->m_leaderboardType ][ pUsers[ pWrite->m_userIdx ].m_profileId ] = pOutput->mRecordId;
			_this->StartNextStatsWrite( taskId, pTask, pWriteInfo );
		}
		else
		{
			_this->UpdateTaskError(taskId,eCLE_InternalError);
			_this->StopTaskRunning(taskId);
		}
	}
}

void CCryGameSpyStats::StatsUpdateCallback(SAKE sake, SAKERequest request, SAKERequestResult result, void* pInputData, void* pOutputData, void* pUserData)
{
	CCryGameSpyStats* _this = static_cast<CCryGameSpyStats *>(pUserData);
	SAKEUpdateRecordInput* pInput = (SAKEUpdateRecordInput *)pInputData;
	// No output for record update

	CryStatsTaskID taskId = _this->FindTaskFromRequest(request);
	if (taskId != CryStatsInvalidTaskID)
	{
		STask* pTask = &_this->m_task[taskId];
		SCryGameSpyStatsUserDataWriteInfo*	pWriteInfo = ( SCryGameSpyStatsUserDataWriteInfo* )_this->m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_INFO ] );

		if (result == SAKERequestResult_SUCCESS)
		{
			_this->StartNextStatsWrite( taskId, pTask, pWriteInfo );
		}
		else
		{
			if(result == SAKERequestResult_NO_PERMISSION)
			{
				_this->UpdateTaskError(taskId,eCLE_InsufficientPrivileges);
			}
			else
			{
				_this->UpdateTaskError(taskId,eCLE_InternalError);
			}

			_this->StopTaskRunning(taskId);
		}
	}
}


void CCryGameSpyStats::StartNextStatsWrite( CryStatsTaskID taskId, STask* pTask, SCryGameSpyStatsUserDataWriteInfo* pWriteInfo )
{
	++( pWriteInfo->m_curUserData );

	if ( pWriteInfo->m_curUserData < pWriteInfo->m_numUserData )
	{
		StartCurrentStatsWrite( taskId, pTask );
	}
	else
	{
		pTask->m_sakeRequest = NULL;
		StopTaskRunning( taskId );
	}
}

void CCryGameSpyStats::LeaderBoardGetCallback(SAKE sake, SAKERequest request, SAKERequestResult result, void* pInputData, void* pOutputData, void* pUserData)
{
	bool dedicated = gEnv->IsDedicated();
	CCryGameSpyStats* _this = static_cast<CCryGameSpyStats *>(pUserData);
	SAKEGetMyRecordsInput* pGetInput = NULL;
	SAKEGetMyRecordsOutput* pGetOutput = NULL;
	SAKESearchForRecordsInput* pSearchInput = NULL;
	SAKESearchForRecordsOutput* pSearchOutput = NULL;

	if ( dedicated )
	{
		pSearchInput = static_cast< SAKESearchForRecordsInput* >( pInputData );
		pSearchOutput = static_cast< SAKESearchForRecordsOutput* >( pOutputData );
	}
	else
	{
		pGetInput = static_cast< SAKEGetMyRecordsInput* >( pInputData );;
		pGetOutput = static_cast< SAKEGetMyRecordsOutput* >( pOutputData );
	}

	CryStatsTaskID taskId = _this->FindTaskFromRequest(request);
	if (taskId != CryStatsInvalidTaskID)
	{
		STask* pTask = &_this->m_task[taskId];

		if (result == SAKERequestResult_SUCCESS)
		{
			SUser* pUsers = (SUser*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_WRITE_LEADERBOARD_USERS]);
			SCryGameSpyStatsBoardWriteInfo* pWriteInfo = (SCryGameSpyStatsBoardWriteInfo*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_WRITE_LEADERBOARD_INFO]);
			SCryGameSpyOneLeaderBoardWrite* pCurBoard = &pWriteInfo->m_pLeaderBoards[pWriteInfo->m_curBoard];
			SRegisterLeaderBoardData* pRegisteredBoard = _this->FindRegisteredBoardInfo(pCurBoard->m_sakeCreateInput.mTableId);

			if (pRegisteredBoard)
			{
				SAKEField* pRecordField = NULL;

				if ( dedicated )
				{
					if (pSearchOutput->mNumRecords==1)				// We have a record store its record id away
					{
						pRecordField = _this->FindFieldFromName(pSearchOutput->mRecords[0],pSearchInput->mNumFields,pGameSpyFieldRecordName);
					}
				}
				else
				{
					if (pGetOutput->mNumRecords==1)				// We have a record store its record id away
					{
						pRecordField = _this->FindFieldFromName(pGetOutput->mRecords[0],pGetInput->mNumFields,pGameSpyFieldRecordName);
					}
				}

				if (pRecordField)
				{
					pRegisteredBoard->m_recordIdCache[ pUsers[ pCurBoard->m_userIdx ].m_profileId ] = pRecordField->mValue.mInt;
				}

				if (pRecordField)
				{
					pTask->m_sakeRequest = _this->UpdateLeaderBoardRecord(pTask, pWriteInfo);
					if (pTask->m_sakeRequest == NULL)
					{
						_this->UpdateTaskError(taskId,eCLE_InternalError);
					}
				}
				else
				{
					pTask->m_sakeRequest = _this->CreateLeaderBoardRecord(pWriteInfo);
					if (pTask->m_sakeRequest == NULL)
					{
						_this->UpdateTaskError(taskId,eCLE_InternalError);
					}
				}
			}
			else
			{
				_this->UpdateTaskError(taskId,eCLE_InvalidParam);
			}
		}
		else
		{
			_this->UpdateTaskError(taskId,eCLE_InternalError);
		}

		if (pTask->error != eCLE_Success)
		{
			_this->StopTaskRunning(taskId);
		}
	}
}

void CCryGameSpyStats::LeaderBoardCreateCallback(SAKE sake, SAKERequest request, SAKERequestResult result, void* pInputData, void* pOutputData, void* pUserData)
{
	bool done = false;
	CCryGameSpyStats* _this = static_cast<CCryGameSpyStats *>(pUserData);
	SAKECreateRecordInput* pInput = (SAKECreateRecordInput *)pInputData;
	SAKECreateRecordOutput* pOutput= (SAKECreateRecordOutput *)pOutputData;

	CryStatsTaskID taskId = _this->FindTaskFromRequest(request);
	if (taskId != CryStatsInvalidTaskID)
	{
		STask* pTask = &_this->m_task[taskId];

		if (result == SAKERequestResult_SUCCESS)
		{
			SUser* pUsers = (SUser*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_WRITE_LEADERBOARD_USERS]);
			SCryGameSpyStatsBoardWriteInfo* pWriteInfo = (SCryGameSpyStatsBoardWriteInfo*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_WRITE_LEADERBOARD_INFO]);
			SCryGameSpyOneLeaderBoardWrite* pCurBoard = &pWriteInfo->m_pLeaderBoards[pWriteInfo->m_curBoard];
			SRegisterLeaderBoardData* pRegisteredBoard = _this->FindRegisteredBoardInfo(pCurBoard->m_sakeCreateInput.mTableId);

			if (pRegisteredBoard)
			{
				pRegisteredBoard->m_recordIdCache[ pUsers[ pCurBoard->m_userIdx ].m_profileId ] = pOutput->mRecordId;
				pWriteInfo->m_curBoard++;
				done = _this->QueueNextLeaderBoard(taskId,pWriteInfo,&pTask->m_sakeRequest);
			}
			else
			{
				_this->UpdateTaskError(taskId,eCLE_InternalError);
			}
		}
		else
		{
			_this->UpdateTaskError(taskId,eCLE_InternalError);
		}

		if ((pTask->error != eCLE_Success) || done)
		{
			_this->StopTaskRunning(taskId);
		}
	}
}

void CCryGameSpyStats::LeaderBoardUpdateCallback(SAKE sake, SAKERequest request, SAKERequestResult result, void* pInputData, void* pOutputData, void* pUserData)
{
	bool done = false;
	CCryGameSpyStats* _this = static_cast<CCryGameSpyStats *>(pUserData);
	SAKEUpdateRecordInput* pInput = (SAKEUpdateRecordInput *)pInputData;
	// No output for record update

	CryStatsTaskID taskId = _this->FindTaskFromRequest(request);
	if (taskId != CryStatsInvalidTaskID)
	{
		STask* pTask = &_this->m_task[taskId];

		if (result == SAKERequestResult_SUCCESS)
		{
			SCryGameSpyStatsBoardWriteInfo* pWriteInfo = (SCryGameSpyStatsBoardWriteInfo*)_this->m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_WRITE_LEADERBOARD_INFO]);
			SCryGameSpyOneLeaderBoardWrite* pCurBoard = &pWriteInfo->m_pLeaderBoards[pWriteInfo->m_curBoard];
			SRegisterLeaderBoardData* pRegisteredBoard = _this->FindRegisteredBoardInfo(pCurBoard->m_sakeCreateInput.mTableId);

			if (pRegisteredBoard)
			{
				pWriteInfo->m_curBoard++;
				done = _this->QueueNextLeaderBoard(taskId,pWriteInfo,&pTask->m_sakeRequest);
			}
			else
			{
				_this->UpdateTaskError(taskId,eCLE_InternalError);
			}
		}
		else if(result == SAKERequestResult_NO_PERMISSION)
		{
			_this->UpdateTaskError(taskId,eCLE_InsufficientPrivileges);
		}
		else
		{
			_this->UpdateTaskError(taskId,eCLE_InternalError);
		}

		if ((pTask->error != eCLE_Success) || done)
		{
			_this->StopTaskRunning(taskId);
		}
	}
}

void CCryGameSpyStats::PopulateUserDataWriteFields( CryStatsTaskID sTaskID, SCryGameSpyOneUserDataWrite* pWrite, int* pNumFields )
{
	STask*	pTask = &m_task[ sTaskID ];
	SUser*	pUsers = ( SUser* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_USERS ] );

	*pNumFields = 0;

	if ( pWrite->m_leaderboardType == eCLLT_Dedicated )
	{
		pWrite->m_sakeField[ *pNumFields ].mName = const_cast< char* >( pGameSpyFieldDedicatedOwnerIDName );
		pWrite->m_sakeField[ *pNumFields ].mType = SAKEFieldType_INT;
		pWrite->m_sakeField[ *pNumFields ].mValue.mInt = pUsers[ pWrite->m_userIdx ].m_profileId;
		++*pNumFields;
	}

	pWrite->m_sakeField[ *pNumFields ].mName = const_cast< char* >(pGameSpyStatsFieldName);
	pWrite->m_sakeField[ *pNumFields ].mType = SAKEFieldType_BINARY_DATA;
	pWrite->m_sakeField[ *pNumFields ].mValue.mBinaryData.mLength = MAX_GAMESPY_PROFILE_SIZE;
	pWrite->m_sakeField[ *pNumFields ].mValue.mBinaryData.mValue = (gsi_u8*)pWrite->m_pData;
	++*pNumFields;
}

SAKERequest CCryGameSpyStats::UpdateStatsRecord(CryStatsTaskID sTaskID, SCryGameSpyOneUserDataWrite* pWrite)
{
	STask*	pTask = &m_task[ sTaskID ];
	SUser*	pUsers = ( SUser* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_USERS ] );

	if ( pWrite->m_leaderboardType ==  eCLLT_P2P )
	{
		pWrite->m_sakeUpdateInput.mTableId = const_cast<char *>( m_pGameSpyStatsP2PTableName );
	}
	else
	{
		pWrite->m_sakeUpdateInput.mTableId = const_cast<char *>( m_pGameSpyStatsDedicatedTableName );
	}

	pWrite->m_sakeUpdateInput.mRecordId = m_statsRecordIdCache[ pWrite->m_leaderboardType ][ pUsers[ pWrite->m_userIdx ].m_profileId ];
	pWrite->m_sakeUpdateInput.mFields = pWrite->m_sakeField;
	PopulateUserDataWriteFields( sTaskID, pWrite, &pWrite->m_sakeUpdateInput.mNumFields );
	return sakeUpdateRecord( m_sake, &pWrite->m_sakeUpdateInput, StatsUpdateCallback, this );
}

SAKERequest CCryGameSpyStats::CreateStatsRecord(CryStatsTaskID sTaskID, SCryGameSpyOneUserDataWrite* pWrite)
{
	if ( pWrite->m_leaderboardType ==  eCLLT_P2P )
	{
		pWrite->m_sakeCreateInput.mTableId = const_cast<char *>( m_pGameSpyStatsP2PTableName );
	}
	else
	{
		pWrite->m_sakeCreateInput.mTableId = const_cast<char *>( m_pGameSpyStatsDedicatedTableName );
	}

	pWrite->m_sakeCreateInput.mFields = pWrite->m_sakeField;
	PopulateUserDataWriteFields( sTaskID, pWrite, &pWrite->m_sakeCreateInput.mNumFields );
	return sakeCreateRecord( m_sake, &pWrite->m_sakeCreateInput, StatsCreateCallback, this );
}

SAKERequest CCryGameSpyStats::FindLeaderBoardRecord(CryStatsTaskID sTaskID, SCryGameSpyStatsBoardWriteInfo* pWriteInfo)
{
	SAKERequest											result;
	STask*													pTask = &m_task[ sTaskID ];
	SUser*													pUsers = ( SUser* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_USERS ] );
	SCryGameSpyOneLeaderBoardWrite* pCurBoard = &pWriteInfo->m_pLeaderBoards[pWriteInfo->m_curBoard];

	if ( pCurBoard->m_leaderboardType == eCLLT_Dedicated )
	{
		pCurBoard->m_sakeSearchInput.mTableId = pCurBoard->m_sakeCreateInput.mTableId;
		pCurBoard->m_sakeSearchInput.mFieldNames = pCurBoard->m_pFieldNames;
		pCurBoard->m_sakeSearchInput.mFieldNames[ 0 ] = const_cast< char* >( pGameSpyFieldRecordName );
		pCurBoard->m_sakeSearchInput.mNumFields = 1;
		pCurBoard->m_sakeSearchInput.mFilter = pCurBoard->m_filter;
		sprintf_s( pCurBoard->m_filter, sizeof ( pCurBoard->m_filter ), pGameSpySearchDedicatedFormat, pCurBoard->m_sakeField[ 0 ].mValue.mInt );
		pCurBoard->m_sakeSearchInput.mSort = const_cast< char* >( pGameSpyTableOrder );
		pCurBoard->m_sakeSearchInput.mOffset = 0;
		pCurBoard->m_sakeSearchInput.mMaxRecords = 1;
		pCurBoard->m_sakeSearchInput.mTargetRecordFilter = NULL;
		pCurBoard->m_sakeSearchInput.mSurroundingRecordsCount = 0;
		pCurBoard->m_sakeSearchInput.mOwnerIds = NULL;
		pCurBoard->m_sakeSearchInput.mNumOwnerIds = 0;
		pCurBoard->m_sakeSearchInput.mCacheFlag = 0;
		result = sakeSearchForRecords( m_sake, &pCurBoard->m_sakeSearchInput, LeaderBoardGetCallback, this );
	}
	else
	{
		pCurBoard->m_sakeGetInput.mTableId = pCurBoard->m_sakeCreateInput.mTableId;
		pCurBoard->m_sakeGetInput.mFieldNames = pCurBoard->m_pFieldNames;
		pCurBoard->m_sakeGetInput.mFieldNames[0]=const_cast<char *>(pGameSpyFieldRecordName);
		pCurBoard->m_sakeGetInput.mNumFields = 1;
		result = sakeGetMyRecords(m_sake,&pCurBoard->m_sakeGetInput,LeaderBoardGetCallback,this);
	}

	return result;
}

SAKERequest CCryGameSpyStats::UpdateLeaderBoardRecord(STask* pTask, SCryGameSpyStatsBoardWriteInfo* pWriteInfo)
{
	SUser* pUsers = ( SUser* )m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_WRITE_LEADERBOARD_USERS]);
	SCryGameSpyOneLeaderBoardWrite* pCurBoard = &pWriteInfo->m_pLeaderBoards[pWriteInfo->m_curBoard];
	SRegisterLeaderBoardData* pRegisteredBoard = FindRegisteredBoardInfo(pCurBoard->m_sakeCreateInput.mTableId);

	pCurBoard->m_sakeUpdateInput.mRecordId = pRegisteredBoard->m_recordIdCache[ pUsers[ pCurBoard->m_userIdx ].m_profileId ];

	return sakeUpdateRecord(m_sake,&pCurBoard->m_sakeUpdateInput,LeaderBoardUpdateCallback,this);
}

SAKERequest CCryGameSpyStats::CreateLeaderBoardRecord(SCryGameSpyStatsBoardWriteInfo* pWriteInfo)
{
	SCryGameSpyOneLeaderBoardWrite* pCurBoard = &pWriteInfo->m_pLeaderBoards[pWriteInfo->m_curBoard];

	return sakeCreateRecord(m_sake,&pCurBoard->m_sakeCreateInput,LeaderBoardCreateCallback,this);
}

void CCryGameSpyStats::FindOwnStatsRecord( STask* pTask, SCryGameSpyOneUserDataWrite* pWrite )
{
	if ( pWrite->m_leaderboardType ==  eCLLT_P2P )
	{
		pWrite->m_sakeGetInput.mTableId = const_cast<char *>( m_pGameSpyStatsP2PTableName );
	}
	else
	{
		pWrite->m_sakeGetInput.mTableId = const_cast<char *>( m_pGameSpyStatsDedicatedTableName );
	}

	pWrite->m_sakeGetInput.mFieldNames = pWrite->m_pFieldNames;
	pWrite->m_sakeGetInput.mFieldNames[ 0 ]=const_cast< char* >( pGameSpyStatsFieldName );
	pWrite->m_sakeGetInput.mFieldNames[ 1 ]=const_cast< char* >( pGameSpyFieldRecordName );
	pWrite->m_sakeGetInput.mNumFields = 2;
	pTask->m_sakeRequest = sakeGetMyRecords( m_sake, &pWrite->m_sakeGetInput, StatsGetCallback, this );
}

void CCryGameSpyStats::FindOthersStatsRecord( STask* pTask, SCryGameSpyOneUserDataWrite* pWrite )
{
	SUser*		pUsers = ( SUser* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_USERS ] );

	if ( pWrite->m_leaderboardType ==  eCLLT_P2P )
	{
		pWrite->m_sakeSearchInput.mTableId = const_cast<char *>( m_pGameSpyStatsP2PTableName );
	}
	else
	{
		pWrite->m_sakeSearchInput.mTableId = const_cast<char *>( m_pGameSpyStatsDedicatedTableName );
	}

	pWrite->m_sakeSearchInput.mFieldNames = pWrite->m_pFieldNames;
	pWrite->m_sakeSearchInput.mFieldNames[ 0 ] = const_cast< char* >( pGameSpyFieldRecordName );
	pWrite->m_sakeSearchInput.mNumFields = 1;
	pWrite->m_sakeSearchInput.mFilter = pWrite->m_filter;
	sprintf_s( pWrite->m_filter, sizeof ( pWrite->m_filter ), pGameSpySearchDedicatedFormat, pUsers[ pWrite->m_userIdx ].m_profileId );
	pWrite->m_sakeSearchInput.mSort = const_cast< char* >( pGameSpyStatsOrder );
	pWrite->m_sakeSearchInput.mOffset = 0;
	pWrite->m_sakeSearchInput.mMaxRecords = 1;
	pWrite->m_sakeSearchInput.mTargetRecordFilter = NULL;
	pWrite->m_sakeSearchInput.mSurroundingRecordsCount = 0;
	pWrite->m_sakeSearchInput.mOwnerIds = NULL;
	pWrite->m_sakeSearchInput.mNumOwnerIds = 0;
	pWrite->m_sakeSearchInput.mCacheFlag = 0;
	pTask->m_sakeRequest = sakeSearchForRecords( m_sake, &pWrite->m_sakeSearchInput, StatsGetCallback, this );
}

void CCryGameSpyStats::StartCurrentStatsWrite( CryStatsTaskID sTaskID, STask* pTask )
{
	SUser*															pUsers = ( SUser* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_USERS ] );
	SCryGameSpyStatsUserDataWriteInfo*	pWriteInfo = ( SCryGameSpyStatsUserDataWriteInfo* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_WRITE_USERDATA_INFO ] );
	SCryGameSpyOneUserDataWrite*				pWrite = &pWriteInfo->m_pUserData[ pWriteInfo->m_curUserData ];

	if ( !( pUsers[ pWrite->m_userIdx ].m_info & eUIB_GPProfile ) )
	{
		CryUserID	userID = GetService()->GetUserID( pUsers[ pWrite->m_userIdx ].m_localIndex );

		if ( userID.IsValid() )
		{
			pUsers[ pWrite->m_userIdx ].m_info |= eUIB_GPProfile;
			pUsers[ pWrite->m_userIdx ].m_profileId = static_cast< const SCryGameSpyUserID* >( userID.get() )->m_profileID;
		}	
	}

	if ( pUsers[ pWrite->m_userIdx ].m_info & eUIB_GPProfile )
	{
		if ( pWrite->m_leaderboardType == eCLLT_Dedicated )
		{
			pWrite->m_sakeField[ 0 ].mValue.mInt = pUsers[ pWrite->m_userIdx ].m_profileId;
		}

		if ( m_statsRecordIdCache[ pWrite->m_leaderboardType ].find( pUsers[ pWrite->m_userIdx ].m_profileId ) != m_statsRecordIdCache[ pWrite->m_leaderboardType ].end() )
		{
			pTask->m_sakeRequest = UpdateStatsRecord( sTaskID, pWrite );
		}
		else
		{
			if ( pWrite->m_leaderboardType == eCLLT_P2P )
			{
				FindOwnStatsRecord( pTask, pWrite );
			}
			else
			{
				FindOthersStatsRecord( pTask, pWrite );
			}
		}

		if ( pTask->m_sakeRequest == NULL )
		{
			UpdateTaskError(sTaskID, eCLE_InternalError);
		}
	}
	else
	{
		UpdateTaskError(sTaskID, eCLE_UserNotSignedIn);
	}

	NetLog("[Lobby] StartStatsWriteUserData result %d", pTask->error);

	if (pTask->canceled || pTask->error != eCLE_Success)
	{
		StopTaskRunning(sTaskID);
	}
}

void CCryGameSpyStats::StartStatsWriteUserData(CryStatsTaskID sTaskID)
{
	STask* pTask = &m_task[sTaskID];

	StartCurrentStatsWrite( sTaskID, pTask );
}

ECryLobbyError CCryGameSpyStats::StatsReadUserData(uint32 user, CryLobbyTaskID* pTaskID, CryStatsReadUserDataCallback cb, void* pCbArg)
{
	SUser		userInfo;

	userInfo.m_info = eUIB_LocalIndex;
	userInfo.m_localIndex = user;

	return StatsReadUserData( &userInfo, pTaskID, cb, pCbArg );
}

ECryLobbyError CCryGameSpyStats::StatsReadUserData(uint32 user, CryUserID userID, CryLobbyTaskID* pTaskID, CryStatsReadUserDataCallback cb, void* pCbArg)
{
	SUser		userInfo;

	userInfo.m_info = eUIB_GPProfile;
	userInfo.m_profileId = static_cast< const SCryGameSpyUserID* >( userID.get() )->m_profileID;

	return StatsReadUserData( &userInfo, pTaskID, cb, pCbArg );
}

ECryLobbyError CCryGameSpyStats::StatsReadUserData( SUser* pUser, CryLobbyTaskID* pTaskID, CryStatsReadUserDataCallback cb, void* pCbArg)
{
	LOBBY_AUTO_LOCK;

	CryStatsTaskID sTaskID;
	SCryGameSpyStatsUserReadInfo* pReadInfo = NULL;
	ECryLobbyError error = StartTask(eT_StatsReadUserData, false, 0, &sTaskID, pTaskID, CrySessionInvalidHandle, (void*)cb, pCbArg);

	if (error == eCLE_Success)
	{
		STask* pTask = &m_task[sTaskID];

		pTask->m_leaderboardType = m_leaderboardType;

		error = CreateTaskParamMem(sTaskID, STATS_PARAM_READ_USERDATA_ENTRY, NULL, sizeof(SCryGameSpyStatsUserReadInfo));
		if (error == eCLE_Success)
		{
			error = CreateTaskParamMem(sTaskID, STATS_PARAM_READ_USERDATA_RESULTS, NULL, m_numUserData * sizeof(SCryLobbyUserData));
			if (error == eCLE_Success)
			{
				error = CreateTaskParamMem(sTaskID, STATS_PARAM_READ_USERDATA_DECRYPT_BUFFER, NULL, MAX_GAMESPY_PROFILE_SETTING_BUFFER_SIZE);
				if (error == eCLE_Success)
				{
					error = CreateTaskParamMem( sTaskID, STATS_PARAM_READ_USERDATA_USER, pUser, sizeof ( SUser ) );
					if ( error == eCLE_Success )
					{
						pReadInfo = (SCryGameSpyStatsUserReadInfo*)m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_READ_USERDATA_ENTRY]);
						memset(pReadInfo, 0, sizeof(SCryGameSpyStatsUserReadInfo));

						if ( pTask->m_leaderboardType == eCLLT_P2P )
						{
							pReadInfo->m_sakeGetInput.mTableId = const_cast<char *>(m_pGameSpyStatsP2PTableName);
							pReadInfo->m_sakeGetInput.mFieldNames = pReadInfo->m_pFieldNames;
							pReadInfo->m_sakeGetInput.mFieldNames[0]=const_cast<char *>(pGameSpyStatsFieldName);
							pReadInfo->m_sakeGetInput.mFieldNames[1]=const_cast<char *>(pGameSpyFieldRecordName);
							pReadInfo->m_sakeGetInput.mNumFields = 2;
						}
						else
						{
							pReadInfo->m_sakeSearchInput.mTableId = const_cast<char *>( m_pGameSpyStatsDedicatedTableName );
							pReadInfo->m_sakeSearchInput.mFieldNames = pReadInfo->m_pFieldNames;
							pReadInfo->m_sakeSearchInput.mFieldNames[ 0 ] = const_cast< char* >( pGameSpyStatsFieldName );
							pReadInfo->m_sakeSearchInput.mFieldNames[ 1 ] = const_cast< char* >( pGameSpyFieldRecordName );
							pReadInfo->m_sakeSearchInput.mNumFields = 2;
							pReadInfo->m_sakeSearchInput.mFilter = pReadInfo->m_filter;
							pReadInfo->m_sakeSearchInput.mSort = const_cast< char* >( pGameSpyStatsOrder );
							pReadInfo->m_sakeSearchInput.mOffset = 0;
							pReadInfo->m_sakeSearchInput.mMaxRecords = 1;
							pReadInfo->m_sakeSearchInput.mTargetRecordFilter = NULL;
							pReadInfo->m_sakeSearchInput.mSurroundingRecordsCount = 0;
							pReadInfo->m_sakeSearchInput.mOwnerIds = NULL;
							pReadInfo->m_sakeSearchInput.mNumOwnerIds = 0;
							pReadInfo->m_sakeSearchInput.mCacheFlag = 0;
						}

						FROM_GAME_TO_LOBBY(&CCryGameSpyStats::StartTaskRunning, this, sTaskID);
					}
				}
			}
		}

		if (error != eCLE_Success)
		{
			FreeTask(sTaskID);
		}
	}

	NetLog("[Lobby] Start StatsReadUserData return %d", error);

	return error;
}


void CCryGameSpyStats::StartStatsReadUserData(CryStatsTaskID sTaskID)
{
	STask* pTask = &m_task[sTaskID];

	SCryGameSpyStatsUserReadInfo* pReadInfo = (SCryGameSpyStatsUserReadInfo*)m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_READ_USERDATA_ENTRY]);
	SUser*												pUser =  ( SUser* )m_pLobby->MemGetPtr( pTask->paramsMem[ STATS_PARAM_READ_USERDATA_USER ] );

	if ( !( pUser->m_info & eUIB_GPProfile ) )
	{
		CryUserID	userID = GetService()->GetUserID( pUser->m_localIndex );

		if ( userID.IsValid() )
		{
			pUser->m_info |= eUIB_GPProfile;
			pUser->m_profileId = static_cast< const SCryGameSpyUserID* >( userID.get() )->m_profileID;
		}
	}

	if ( pUser->m_info & eUIB_GPProfile )
	{
		pReadInfo->m_profileId = pUser->m_profileId;

		if ( pTask->m_leaderboardType == eCLLT_P2P )
		{
			pTask->m_sakeRequest = sakeGetMyRecords(m_sake,&pReadInfo->m_sakeGetInput,ReadUserDataCallback,this);
		}
		else
		{
			sprintf_s( pReadInfo->m_filter, sizeof ( pReadInfo->m_filter ), pGameSpySearchDedicatedFormat, pReadInfo->m_profileId );
			pTask->m_sakeRequest = sakeSearchForRecords( m_sake, &pReadInfo->m_sakeSearchInput, ReadUserDataCallback, this );
		}

		if (pTask->m_sakeRequest == NULL)
		{
			UpdateTaskError(sTaskID, eCLE_InternalError);
		}
	}
	else
	{
		UpdateTaskError( sTaskID, eCLE_UserNotSignedIn );
	}

	NetLog("[Lobby] StartStatsReadLeaderBoardByRankForUser result %d", pTask->error);

	if (pTask->canceled || pTask->error != eCLE_Success)
	{
		StopTaskRunning(sTaskID);
	}
}

void CCryGameSpyStats::EndStatsReadUserData(CryStatsTaskID sTaskID)
{
	STask* pTask = &m_task[sTaskID];
	SCryLobbyUserData* pResults = (SCryLobbyUserData*)m_pLobby->MemGetPtr(pTask->paramsMem[STATS_PARAM_READ_USERDATA_RESULTS]);

	((CryStatsReadUserDataCallback)pTask->pCb)(pTask->lTaskID, pTask->error, pResults, m_numUserData, pTask->pCbArg);
}

void CCryGameSpyStats::OnGameSpyObjectAvailable( EGameSpyObjectBit object )
{
	if ( object & eGSOB_GPConnection )
	{
		for ( uint32 i = 0; i < MAX_STATS_TASKS; ++i )
		{
			STask* pTask = &m_task[ i ];

			if ( pTask->used )
			{
				pTask->m_haveGPConnection = true;
			}
		}
	}
}

void CCryGameSpyStats::OnGameSpyObjectUnavailable( EGameSpyObjectBit object )
{
	if ( object & eGSOB_GPConnection )
	{
		for ( uint32 i = 0; i < MAX_STATS_TASKS; ++i )
		{
			STask* pTask = &m_task[ i ];

			if ( pTask->used )
			{
				pTask->m_haveGPConnection = false;

				switch ( pTask->startedTask )
				{
				case eT_StatsWriteLeaderBoard:
				case eT_StatsReadLeaderBoard:
				case eT_StatsWriteUserData:
				case eT_StatsReadUserData:
					if ( pTask->m_taskStarted )
					{
						UpdateTaskError( i, eCLE_UserNotSignedIn );
					}
					break;
				default:
					NetLogAlways( "[GameSpy] Task type %d not handled by CCryGameSpyStats::OnGameSpyObjectUnavailable", pTask->startedTask );
					break;
				}
			}
		}
	}
}

void CCryGameSpyStats::OnGameSpyObjectCantMakeAvailable( EGameSpyObjectBit object )
{
	if ( object & eGSOB_GPConnection )
	{
		for ( CryStatsTaskID id = 0; id < MAX_STATS_TASKS; ++id )
		{
			STask*	pTask = &m_task[ id ];

			if ( pTask->used )
			{
				switch ( pTask->startedTask )
				{
				case eT_StatsWriteLeaderBoard:
				case eT_StatsReadLeaderBoard:
				case eT_StatsWriteUserData:
				case eT_StatsReadUserData:
					UpdateTaskError( id, eCLE_UserNotSignedIn );
					break;
				default:
					NetLogAlways( "[GameSpy] Task type %d not handled by CCryGameSpyStats::OnGameSpyObjectCantMakeAvailable", pTask->startedTask );
					break;
				}
			}
		}
	}
}

void CCryGameSpyStats::SetLeaderboardType( ECryLobbyLeaderboardType leaderboardType )
{
	m_leaderboardType = leaderboardType;
}

ECryLobbyLeaderboardType CCryGameSpyStats::GetLeaderboardType()
{
	return m_leaderboardType;
}

SAKERequest CCryGameSpyStats::SakeSearchForRecords( SAKESearchForRecordsInput* pInput, SAKERequestCallback callback, void* pUserData )
{
	SAKERequest		result = NULL;

	if ( m_sake )
	{
		result = sakeSearchForRecords( m_sake, pInput, callback, pUserData );
	}

	return result;
}

ECryLobbyError CCryGameSpyStats::CreateTaskLink( STask* pTask )
{
	ECryLobbyError		error;

	if ( pTask && !pTask->m_pLink )
	{
		STaskLink*				pLink = new STaskLink();

		if ( pLink )
		{
			pLink->pStats = this;
			pLink->pTask = pTask;
			pTask->m_pLink = pLink;
			error = eCLE_Success;
		}
		else
		{
			error = eCLE_OutOfMemory;
		}
	}
	else
	{
		error = eCLE_InvalidParam;
	}

	return error;
}

void CCryGameSpyStats::PassTaskLinkToGameSpy( const _smart_ptr< STaskLink>& pLink )
{
	m_taskLinks.push_back( pLink );
}

void CCryGameSpyStats::FreeTaskLinkFromGameSpy( STaskLink* pLink )
{
	uint32			i;

	for ( i = 0; i < m_taskLinks.size(); ++i )
	{
		if ( m_taskLinks[ i ].get() == pLink )
		{
			if ( i < m_taskLinks.size() - 1 )
			{
				m_taskLinks[ i ] = m_taskLinks[ m_taskLinks.size() - 1 ];
				m_taskLinks.resize( m_taskLinks.size() - 1 );
			}
			else
			{
				m_taskLinks.pop_back();
			}

			break;
		}
	}
}

void CCryGameSpyStats::FreeTask( CryStatsTaskID sTaskID )
{
	STask*					pTask = &m_task[ sTaskID ];

	if ( pTask->m_pLink )
	{
		pTask->m_pLink->pTask = NULL;
		pTask->m_pLink = NULL;
	}

	CCryStats::FreeTask( sTaskID );
}

#endif //USE_CRYLOBBY_GAMESPY
